#!/usr/bin/env python3
"""Round-trip solution documentation between Markdown and the static site."""

from __future__ import annotations

import argparse
import html
import json
import re
from pathlib import Path


SITE_DIR = Path(__file__).resolve().parents[1]


def _infer_solution_name(site_dir: Path) -> str:
    """Return the solution folder name, i.e. the first path component under
    ``Solutions/``. This keeps the script solution-agnostic and robust to the
    site being nested under a per-concern folder such as ``Documentation/``.
    """
    parts = site_dir.parts
    if "Solutions" in parts:
        index = parts.index("Solutions")
        if index + 1 < len(parts):
            return parts[index + 1]
    return site_dir.parent.name


SOLUTION_NAME = _infer_solution_name(SITE_DIR)
DATA_PATH = SITE_DIR / "content" / "sections.json"
DOC_PATH = SITE_DIR / f"{SOLUTION_NAME}Documentation.md"

EDITABLE_FIELDS = (
    "summary",
    "summaryHtml",
    "image",
    "bodyText",
    "keyPoints",
    "orderedItems",
    "htmlContent",
    "integrationPattern",
)
JSON_FIELDS = {"integrationPattern"}

SECTION_START_RE = re.compile(r"^<!-- DOCS:SECTION id=\"([^\"]+)\" -->$", re.MULTILINE)
FIELD_RE = re.compile(
    r"<!-- site:field ([A-Za-z0-9_]+) -->\n(.*?)\n<!-- /site:field \1 -->",
    re.DOTALL,
)
MARKDOWN_IMAGE_RE = re.compile(r"^!\[[^\]]*\]\(([^)\s]+)(?:\s+\"[^\"]*\")?\)$")


def load_data() -> dict:
    with DATA_PATH.open(encoding="utf-8") as handle:
        return json.load(handle)


def save_data(data: dict) -> None:
    DATA_PATH.write_text(json.dumps(data, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")


def markdown_list(items: list[str], ordered: bool = False) -> str:
    if not items:
        return ""
    if ordered:
        return "\n".join(f"{index}. {item}" for index, item in enumerate(items, start=1))
    return "\n".join(f"- {item}" for item in items)


def parse_markdown_list(value: str) -> list[str]:
    items: list[str] = []
    for line in value.splitlines():
        stripped = line.strip()
        match = re.match(r"^(?:[-*]|\d+[.)])\s+(.*)$", stripped)
        if match:
            items.append(match.group(1).strip())
        elif stripped:
            items.append(stripped)
    return items


def markdown_image(path: str, alt_text: str) -> str:
    if not path:
        return ""
    safe_alt_text = alt_text.replace("[", "(").replace("]", ")")
    return f"![{safe_alt_text}]({path})"


def parse_image_reference(value: str) -> str:
    match = MARKDOWN_IMAGE_RE.match(value.strip())
    if match:
        return match.group(1).strip()
    return value.strip()


def field_block(name: str, value: str) -> str:
    return f"<!-- site:field {name} -->\n{value.strip()}\n<!-- /site:field {name} -->"


def export_document() -> None:
    data = load_data()
    title = data.get("meta", {}).get("title", f"{SOLUTION_NAME} Documentation")
    lines = [
        f"# {SOLUTION_NAME}Documentation",
        "",
        f"This is the editable source document for the {title} site.",
        "",
        "Edit only the content inside `site:field` blocks. Keep the `DOCS:SECTION` and `site:field` comments in place so the site can be regenerated safely.",
        "",
        "Site-managed structure such as section order, groups, related sections, previous/next links, and the index is intentionally not editable here.",
        "",
    ]

    for section in data["sections"]:
        lines.extend(
            [
                f"<!-- DOCS:SECTION id=\"{section['id']}\" -->",
                f"## {section['title']}",
                "",
                f"<!-- site:meta group=\"{section['group']}\" -->",
                "",
                "### Summary",
                field_block("summary", section.get("summary", "")),
                "",
                "### Summary HTML",
                field_block("summaryHtml", section.get("summaryHtml", "")),
                "",
                "### Image Reference",
                field_block("image", markdown_image(section.get("image", ""), f"{section['title']} image")),
                "",
                "### Body Text",
                field_block("bodyText", section.get("bodyText", "")),
                "",
                "### Key Points",
                field_block("keyPoints", markdown_list(section.get("keyPoints", []))),
                "",
                "### Ordered Items",
                field_block("orderedItems", markdown_list(section.get("orderedItems", []), ordered=True)),
                "",
                "### HTML Content",
                field_block("htmlContent", section.get("htmlContent", "")),
                "",
                "### Integration Pattern",
                field_block(
                    "integrationPattern",
                    json.dumps(section.get("integrationPattern", {}), indent=2, ensure_ascii=True)
                    if section.get("integrationPattern")
                    else "",
                ),
                "",
                "<!-- /DOCS:SECTION -->",
                "",
            ]
        )

    DOC_PATH.write_text("\n".join(lines), encoding="utf-8")
    print(f"Exported {len(data['sections'])} sections to {DOC_PATH}")


def parse_document() -> dict[str, dict[str, str]]:
    text = DOC_PATH.read_text(encoding="utf-8")
    matches = list(SECTION_START_RE.finditer(text))
    sections: dict[str, dict[str, str]] = {}

    for index, match in enumerate(matches):
        section_id = match.group(1)
        start = match.end()
        end = matches[index + 1].start() if index + 1 < len(matches) else len(text)
        block = text[start:end]
        end_marker = block.find("<!-- /DOCS:SECTION -->")
        if end_marker != -1:
            block = block[:end_marker]
        fields = {field: value.strip() for field, value in FIELD_RE.findall(block)}
        sections[section_id] = fields

    return sections


def import_document() -> None:
    if not DOC_PATH.exists():
        raise SystemExit(f"Document not found: {DOC_PATH}. Run `export` first.")

    data = load_data()
    document_sections = parse_document()
    known_ids = {section["id"] for section in data["sections"]}
    unknown_ids = sorted(set(document_sections) - known_ids)
    if unknown_ids:
        raise SystemExit(f"Unknown section id(s) in document: {', '.join(unknown_ids)}")

    for section in data["sections"]:
        fields = document_sections.get(section["id"])
        if fields is None:
            raise SystemExit(f"Missing section in document: {section['id']}")

        for field in EDITABLE_FIELDS:
            if field not in fields:
                continue
            raw_value = fields[field].strip()
            if field in {"keyPoints", "orderedItems"}:
                values = parse_markdown_list(raw_value)
                if values:
                    section[field] = values
                else:
                    section.pop(field, None)
            elif field == "image":
                image_path = parse_image_reference(raw_value)
                if image_path:
                    section[field] = image_path
                else:
                    section.pop(field, None)
            elif field in JSON_FIELDS:
                if raw_value:
                    try:
                        section[field] = json.loads(raw_value)
                    except json.JSONDecodeError as exc:
                        raise SystemExit(f"Invalid JSON in {section['id']} {field}: {exc}") from exc
                else:
                    section.pop(field, None)
            elif raw_value:
                section[field] = raw_value
            else:
                section.pop(field, None)

    save_data(data)
    build_site(data)
    print(f"Imported {len(data['sections'])} sections from {DOC_PATH}")


def page_href(section_or_id: dict | str) -> str:
    section_id = section_or_id if isinstance(section_or_id, str) else section_or_id["id"]
    return "index.html" if section_id == "overview" else f"{section_id}.html"


def first_section_href(data: dict, *, section_id: str | None = None, group_contains: str | None = None) -> str:
    for section in data["sections"]:
        if section_id and section["id"] == section_id:
            return page_href(section)
        if group_contains and group_contains.lower() in section.get("group", "").lower():
            return page_href(section)
    return page_href(data["sections"][0])


def page_shell(section: dict, data: dict) -> str:
    meta = data.get("meta", {})
    site_title = meta.get("title", f"{SOLUTION_NAME} Documentation")
    site_description = meta.get("description", "")
    title = f"{section['title']} | {site_title}"
    description = section.get("summary", site_description)
    embedded_json = json.dumps(data, indent=2, ensure_ascii=True)

    return f"""<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{html.escape(title)}</title>
  <meta name="description" content="{html.escape(description, quote=True)}">
  <link rel="stylesheet" href="styles.css">
</head>
<body data-section-id="{html.escape(section['id'], quote=True)}">
  <a class="skip-link" href="#content">Skip to content</a>
  <header class="site-header" id="top">
    <div class="header-copy">
      <h1>{html.escape(site_title)}</h1>
    </div>
  </header>

  <div class="layout">
    <aside class="sidebar" aria-label="Documentation navigation">
      <label class="search-label" for="site-search">Search sections</label>
      <input id="site-search" class="search-input" type="search" placeholder="Filter by topic, module, or capability" autocomplete="off">
      <nav id="sidebar-nav" class="nav-list" aria-label="Sections"></nav>
    </aside>

    <main id="content" class="content" tabindex="-1">
      <div id="sections-root"></div>
    </main>
  </div>

  <div class="modal-backdrop" id="callout-modal" hidden>
    <div class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title" tabindex="-1">
      <button class="modal-close" type="button" data-close-modal aria-label="Close callout">x</button>
      <div id="modal-content"></div>
    </div>
  </div>

  <footer class="site-footer">
    <p>{html.escape(site_title)}.</p>
  </footer>
  <script id="sections-data" type="application/json">{embedded_json}</script>
  <script src="site.js"></script>
</body>
</html>
"""


def build_site(data: dict | None = None) -> None:
    if data is None:
        data = load_data()

    for section in data["sections"]:
        (SITE_DIR / page_href(section)).write_text(page_shell(section, data), encoding="utf-8")

    print(f"Built {len(data['sections'])} HTML pages in {SITE_DIR}")


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("command", choices=("export", "import", "build"))
    args = parser.parse_args()

    if args.command == "export":
        export_document()
    elif args.command == "import":
        import_document()
    elif args.command == "build":
        build_site()


if __name__ == "__main__":
    main()
