# Copyright (C) 2007-2026 Ahmet Öztürk (aoz_2@yahoo.com)
#
# This file is part of Lifeograph.
#
# Lifeograph is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Lifeograph is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Lifeograph.  If not, see <http://www.gnu.org/licenses/>.

from LoG import *
import os
import re
from urllib.parse import urlparse, unquote

NAME="Export Markdown"
VERSION="0.1"
CATEGORY=SCtg.EXPORTER

# get the default extension:
def get_ext():
    return "md"

# main func:
def run(path, entries):
    base_dir = os.path.dirname(path) or "."
    base_name = os.path.basename(path)

    has_md = base_name.lower().endswith(".md")
    stem = base_name[:-3] if has_md else base_name

    for entry in entries:
        md = _export_entry_to_markdown(entry)

        filename = (f"{stem}__{_sanitize_filename(entry.get_name())}.md" if len(entries)>1
                    else f"{stem}.md")

        out_path = os.path.join(base_dir, filename)

        with open(out_path, "w", encoding="utf-8") as f:
            f.write(md)

    return True


def _sanitize_filename(name: str) -> str:
    # remove forbidden characters and collapse whitespace
    name = re.sub(r'[\\/:*?"<>|]', "", name)
    name = re.sub(r"\s+", "_", name.strip())
    return name or "entry"

def _uri_to_path(uri: str) -> str:
    parsed = urlparse(uri)
    return unquote(parsed.path)

def _export_entry_to_markdown(entry):
    out = []
    last_para_was_code = False

    for p in entry:
        text = p.get_text()

        # blockquote
        blockquote = ""
        if p.is_quote():
            blockquote = "> " * p.get_indent_level()

        #image
        if p.is_image(VT.PS.IMAGE_FILE):
            path = _uri_to_path(p.get_uri())
            out.append(f'![image]({path})')

        # indentation (only valid for list tems in MD)
        indent = ""
        if p.get_list_type() != VT.PLS.PLAIN:
            indent = "  " * p.get_indent_level()

        # code lang
        lang_str = p.get_code_lang()

        if p.is_code():
            if not last_para_was_code:
                out.append("")
                out.append(f"``` {lang_str}")
                last_para_was_code = True
        elif last_para_was_code:
            out.append("```")
            out.append("")
            last_para_was_code = False

        # heading level
        h = 0
        heading = ""
        match p.get_heading_level():
            case VT.PHS.TITLE:
                h = 1
            case VT.PHS.LARGE:
                h = 1 if p.has_hrule() else 2 #H2 header with hrule is regarded as H1
            case VT.PHS.MEDIUM:
                h = 3
        if h > 0:
            heading = ("#" * h + " ")

        # list / todo prefix
        list_type = ""
        ps = p.get_list_type()

        TODO_MAP = {
            VT.PLS.TODO: "[ ] ",
            VT.PLS.PROGRESSED: "[-] ",
            VT.PLS.DONE: "[x] ",
            VT.PLS.CANCELED: "[~] ",
        }

        LIST_MAP = {
            VT.PLS.BULLET: "- ",
            VT.PLS.NUMBER: "1. ",
            VT.PLS.LETTER: "a. ",
            VT.PLS.LETTER_SMALL: "A. ",
            VT.PLS.ROMAN: "i. ",
            VT.PLS.ROMAN_SMALL: "I. ",
        }

        if ps in TODO_MAP and h==0:
            list_type = "- " + TODO_MAP[ps]
        elif ps in LIST_MAP:
            list_type = LIST_MAP[ps]

        # inline formatting
        formatted = _apply_inline_formats(text, p.get_formats())

        if not (p.is_empty_completely() and p.has_hrule()):
            out.append(f"{blockquote}{indent}{list_type}{heading}{formatted}")

        if p.has_hrule() and p.get_heading_level() != VT.PHS.TITLE:
            out.append("---")

    return "\n".join(out).rstrip() + "\n"


def _apply_inline_formats(text, formats):
    events = []

    for f in formats:
        start, end, fmt = f.pos_bgn, f.pos_end, f.type

        if fmt == VT.FMT.BOLD:
            events.append((start, "**", True))
            events.append((end, "**", False))
        elif fmt == VT.FMT.ITALIC:
            events.append((start, "*", True))
            events.append((end, "*", False))
        elif fmt == VT.FMT.UNDERLINE:
            events.append((start, "<u>", True))
            events.append((end, "</u>", False))
        elif fmt == VT.FMT.STRIKETHRU:
            events.append((start, "~~", True))
            events.append((end, "~~", False))
        elif fmt == VT.FMT.HIGHLIGHT:
            events.append((start, "==", True))
            events.append((end, "==", False))
        elif fmt == VT.FMT.LINK_URI:
            events.append((start, "[", True))
            events.append((end, f"]({f.uri})", False))

    # openings first, closings last at same index
    events.sort(key=lambda e: (e[0], not e[2]))

    result = []
    pos = 0

    for idx, token, opening in events:
        if idx > pos:
            result.append(text[pos:idx])
            pos = idx
        result.append(token)

    result.append(text[pos:])
    return "".join(result)
