"""Index builder — generates navigable index pages for the vault.

Creates:
- Master index (MOC — Map of Content) with all files organized by folder
- Tag index pages (one page per namespace with all tagged files)
- Orphan index (unlinked files that need attention)
"""

import os
import re
from pathlib import Path
from datetime import datetime, timezone

from .scanner import find_md_files, parse_frontmatter, extract_tags, build_file_index

# Tag namespace → color mapping for Obsidian CSS snippets and display
TAG_COLORS = {
    "domain":   "#4A90D9",   # blue
    "artifact": "#E67E22",   # orange
    "process":  "#27AE60",   # green
    "tech":     "#8E44AD",   # purple
    "meta":     "#95A5A6",   # gray
    "audience": "#E74C3C",   # red
    "course":   "#2ECC71",   # emerald
    "type":     "#F39C12",   # amber
    "status":   "#1ABC9C",   # teal
    "layer":    "#3498DB",   # light blue
}

DEFAULT_COLOR = "#7F8C8D"  # dark gray for unknown namespaces


def get_tag_color(tag):
    """Return hex color for a tag based on its namespace."""
    if "/" in tag:
        ns = tag.split("/")[0]
        return TAG_COLORS.get(ns, DEFAULT_COLOR)
    return DEFAULT_COLOR


def build_master_index(vault_path, output_file=None):
    """Generate a master index (MOC) organized by directory.

    Structure:
    # Vault Index
    ## folder_name/
    - [[file1]] — title (tags colored)
    - [[file2]] — title
    """
    base = Path(vault_path)
    md_files = find_md_files(base)

    # Group by directory
    by_dir = {}
    for f in md_files:
        rel = str(f.relative_to(base)).replace("\\", "/")
        parent = str(f.relative_to(base).parent).replace("\\", "/")
        if parent == ".":
            parent = "(root)"
        by_dir.setdefault(parent, []).append((rel, f))

    lines = [
        "---",
        f'title: "Vault Index"',
        f'generated: "{datetime.now(timezone.utc).isoformat()}"',
        "tags: [meta/index, meta/generated]",
        "---",
        "",
        "# Vault Index",
        "",
        f"> Auto-generated by marginalia. {len(md_files)} files in {len(by_dir)} directories.",
        "",
    ]

    for dir_name in sorted(by_dir.keys()):
        files = by_dir[dir_name]
        lines.append(f"## {dir_name}/")
        lines.append("")
        for rel, filepath in sorted(files, key=lambda x: x[0]):
            try:
                content = filepath.read_text(encoding="utf-8", errors="replace")
                fm = parse_frontmatter(content)
                title = fm.get("title", "").strip('"\'') if fm else filepath.stem
                tags = extract_tags(fm) if fm else []
            except Exception:
                title = filepath.stem
                tags = []

            tag_display = ""
            if tags:
                colored = []
                for t in tags[:5]:  # max 5 tags in display
                    color = get_tag_color(t)
                    colored.append(f"`{t}`")
                tag_display = " " + " ".join(colored)

            # Use relative link for Obsidian compatibility
            lines.append(f"- [{title}]({rel}){tag_display}")

        lines.append("")

    content = "\n".join(lines) + "\n"

    if output_file:
        Path(output_file).write_text(content, encoding="utf-8")
    return content


def build_tag_index(vault_path, output_dir=None):
    """Generate per-namespace tag index pages.

    Creates one page per tag namespace (domain/, artifact/, etc.)
    listing all files with that tag, grouped by tag value.
    """
    base = Path(vault_path)
    md_files = find_md_files(base)

    # Build tag → files map
    tag_files = {}
    for f in md_files:
        try:
            content = f.read_text(encoding="utf-8", errors="replace")
            fm = parse_frontmatter(content)
            if fm:
                rel = str(f.relative_to(base)).replace("\\", "/")
                title = fm.get("title", "").strip('"\'') or f.stem
                for tag in extract_tags(fm):
                    tag_files.setdefault(tag, []).append((rel, title))
        except Exception:
            continue

    # Group by namespace
    namespaces = {}
    flat_tags = {}
    for tag, files in tag_files.items():
        if "/" in tag:
            ns = tag.split("/")[0]
            namespaces.setdefault(ns, {})[tag] = files
        else:
            flat_tags[tag] = files

    pages = {}

    # Per-namespace pages
    for ns_name in sorted(namespaces.keys()):
        ns_tags = namespaces[ns_name]
        color = TAG_COLORS.get(ns_name, DEFAULT_COLOR)

        lines = [
            "---",
            f'title: "Tag Index: {ns_name}"',
            f'generated: "{datetime.now(timezone.utc).isoformat()}"',
            f"tags: [meta/index, meta/generated, meta/tag-index]",
            "---",
            "",
            f"# Tag Index: {ns_name}",
            "",
            f"> Color: {color} | {sum(len(v) for v in ns_tags.values())} file references across {len(ns_tags)} tags",
            "",
        ]

        for tag in sorted(ns_tags.keys()):
            files = ns_tags[tag]
            tag_value = tag.split("/", 1)[1] if "/" in tag else tag
            lines.append(f"### {tag_value}")
            lines.append("")
            for rel, title in sorted(files, key=lambda x: x[1]):
                lines.append(f"- [{title}]({rel})")
            lines.append("")

        content = "\n".join(lines) + "\n"
        filename = f"tags-{ns_name}.md"
        pages[filename] = content

        if output_dir:
            out = Path(output_dir) / filename
            out.parent.mkdir(parents=True, exist_ok=True)
            out.write_text(content, encoding="utf-8")

    # Flat tags page
    if flat_tags:
        lines = [
            "---",
            f'title: "Tag Index: Unnamespaced"',
            f'generated: "{datetime.now(timezone.utc).isoformat()}"',
            "tags: [meta/index, meta/generated, meta/tag-index]",
            "---",
            "",
            "# Tag Index: Unnamespaced Tags",
            "",
            f"> {sum(len(v) for v in flat_tags.values())} file references across {len(flat_tags)} flat tags",
            "> These tags should be migrated to namespaces. Run: `marginalia fix-tags`",
            "",
        ]

        for tag in sorted(flat_tags.keys()):
            files = flat_tags[tag]
            lines.append(f"### {tag}")
            lines.append("")
            for rel, title in sorted(files, key=lambda x: x[1]):
                lines.append(f"- [{title}]({rel})")
            lines.append("")

        content = "\n".join(lines) + "\n"
        pages["tags-flat.md"] = content

        if output_dir:
            out = Path(output_dir) / "tags-flat.md"
            out.write_text(content, encoding="utf-8")

    return pages


def build_orphan_index(vault_path, output_file=None):
    """Generate an index of orphan files (not linked from anywhere)."""
    base = Path(vault_path)
    file_index = build_file_index(base)
    md_files = find_md_files(base)

    # Build inbound link map
    linked_to = set()
    for f in md_files:
        try:
            content = f.read_text(encoding="utf-8", errors="replace")
        except Exception:
            continue
        for m in re.finditer(r"\[([^\]]+)\]\(([^)]+)\)", content):
            target = m.group(2).split("#")[0]
            if not re.match(r"^(https?://|mailto:|#)", target) and target:
                resolved = f.parent / target
                if resolved.exists():
                    try:
                        target_rel = str(resolved.resolve().relative_to(base.resolve())).replace("\\", "/")
                        linked_to.add(target_rel)
                    except (ValueError, OSError):
                        pass
        # Wikilinks
        for m in re.finditer(r"\[\[([^\]|]+)(?:\|[^\]]+)?\]\]", content):
            wiki_target = m.group(1).strip()
            key = (wiki_target + ".md").lower() if not wiki_target.endswith(".md") else wiki_target.lower()
            candidates = file_index.get(key, [])
            if candidates:
                linked_to.add(candidates[0][1])

    all_files = {str(f.relative_to(base)).replace("\\", "/") for f in md_files}
    orphans = sorted(all_files - linked_to)

    lines = [
        "---",
        f'title: "Orphan Files Index"',
        f'generated: "{datetime.now(timezone.utc).isoformat()}"',
        "tags: [meta/index, meta/generated, meta/orphans]",
        "---",
        "",
        "# Orphan Files",
        "",
        f"> {len(orphans)} files not linked from anywhere. Consider linking or archiving them.",
        "",
    ]

    # Group by directory
    by_dir = {}
    for orphan in orphans:
        parent = str(Path(orphan).parent).replace("\\", "/")
        if parent == ".":
            parent = "(root)"
        by_dir.setdefault(parent, []).append(orphan)

    for dir_name in sorted(by_dir.keys()):
        lines.append(f"## {dir_name}/")
        lines.append("")
        for orphan in by_dir[dir_name]:
            lines.append(f"- [{Path(orphan).stem}]({orphan})")
        lines.append("")

    content = "\n".join(lines) + "\n"

    if output_file:
        Path(output_file).write_text(content, encoding="utf-8")
    return content


def generate_obsidian_css_snippet(output_file=None):
    """Generate an Obsidian CSS snippet for colored tags.

    Save to .obsidian/snippets/marginalia-tag-colors.css and enable in
    Obsidian Settings > Appearance > CSS Snippets.
    """
    lines = [
        "/* marginalia tag colors — auto-generated */",
        "/* Enable in Obsidian: Settings > Appearance > CSS Snippets */",
        "",
    ]

    for ns, color in sorted(TAG_COLORS.items()):
        lines.append(f'/* {ns} tags */')
        # Obsidian renders tags as <a class="tag" href="#tag/value">
        lines.append(f'a.tag[href*="#{ns}/"] {{')
        lines.append(f'  background-color: {color}20;')
        lines.append(f'  color: {color};')
        lines.append(f'  border: 1px solid {color}40;')
        lines.append(f'}}')
        lines.append("")

    content = "\n".join(lines) + "\n"

    if output_file:
        Path(output_file).parent.mkdir(parents=True, exist_ok=True)
        Path(output_file).write_text(content, encoding="utf-8")
    return content
