"""Backfill date: and agent: frontmatter fields on existing vault files.

Scans journal/, memory/, and knowledge/ directories and adds missing
`date:` and `agent:` fields based on filename patterns and file type.
"""

from __future__ import annotations

import logging
import re
from pathlib import Path
from typing import TYPE_CHECKING

import frontmatter

if TYPE_CHECKING:
    from agntspace.vault.paths import VaultPaths

log = logging.getLogger(__name__)

# Map file type → agent name
_TYPE_AGENT_MAP: dict[str, str] = {  # arch:deterministic — one-shot legacy migration tool, maps file type to the agent that authored that type under the old pre-v1.4 layout; runs once per vault upgrade and is never read at runtime
    "agent_observations": "main",
    "briefing": "main",
    "eod_report": "ops-supervisor",
    "daily_reflection": "philosopher",
    "weekly_reflection": "philosopher",
    "daily_memory": "main",
    "pending_memory": "main",
    "memory_summary": "main",
}

# Filename patterns → (type, agent) for files without a type: field
_FILENAME_PATTERNS: list[tuple[str, str, str]] = [
    (r"^user-journal_(\d{4}-\d{2}-\d{2})\.md$", "journal", "user"),
    (r"^(\d{4}-\d{2}-\d{2})\.md$", "journal", "user"),  # legacy bare-date files
    (r"^agent-observations_(\d{4}-\d{2}-\d{2})\.md$", "agent_observations", "main"),
    (r"^morning-briefing_(\d{4}-\d{2}-\d{2})\.md$", "briefing", "ops-supervisor"),
    (r"^eod-report_(\d{4}-\d{2}-\d{2})\.md$", "eod_report", "ops-supervisor"),
    (r"^daily-reflection_(\d{4}-\d{2}-\d{2})\.md$", "daily_reflection", "philosopher"),
    (r"^weekly-reflection_([\d\-W]+)\.md$", "weekly_reflection", "philosopher"),
    (r"^memory_(\d{4}-\d{2}-\d{2})\.md$", "daily_memory", "main"),
    (r"^memory-updates_(\d{4}-\d{2}-\d{2})\.md$", "pending_memory", "main"),
]


def _extract_date_from_title(title: str) -> str | None:
    """Extract YYYY-MM-DD from a title like 'Memory — 2026-04-07'."""
    m = re.search(r"(\d{4}-\d{2}-\d{2})", title)
    return m.group(1) if m else None


def _extract_date_from_filename(filename: str) -> str | None:
    """Extract date from filename patterns."""
    for pattern, _, _ in _FILENAME_PATTERNS:
        m = re.match(pattern, filename)
        if m:
            val = m.group(1)
            # Could be YYYY-MM-DD or YYYY-Wnn
            return val
    return None


def _get_agent_for_file(filename: str, metadata: dict) -> str | None:
    """Determine which agent authored this file."""
    file_type = metadata.get("type", "")

    # Check type-based mapping first
    if file_type in _TYPE_AGENT_MAP:
        return _TYPE_AGENT_MAP[file_type]

    # Check filename patterns
    for pattern, _, agent in _FILENAME_PATTERNS:
        if re.match(pattern, filename):
            return agent if agent != "user" else None

    # KB files
    entity_type = metadata.get("entity_type", "")
    if entity_type == "source_summary":
        return "librarian"
    if entity_type in ("person", "organization", "concept", "synthesis", "decision_record"):
        return "editor"
    if entity_type == "project":
        return "wiki-supervisor"
    if metadata.get("type") == "kb_research":
        return "researcher"
    if metadata.get("type") in ("kb_index", "kb_log"):
        return "librarian"
    if metadata.get("type") == "kb_concepts":
        return "editor"

    return None


def migrate_vault(
    vault_dir: Path,
    dry_run: bool = False,
    paths: VaultPaths | None = None,
) -> dict:
    """Scan vault and backfill missing date:/agent: frontmatter fields.

    Returns stats: {scanned, updated, skipped, errors}.
    """
    stats = {"scanned": 0, "updated": 0, "skipped": 0, "errors": 0}
    updated_files: list[str] = []

    def _resolve(logical: str) -> Path:
        if paths:
            return paths.resolve(logical)
        return vault_dir / logical

    # Directories to scan
    scan_dirs = [
        _resolve("journal"),
        _resolve("memory"),
    ]
    # Add all KB wiki dirs
    kb_dir = _resolve("knowledge")
    if kb_dir.is_dir():
        for kb in kb_dir.iterdir():
            if kb.is_dir():
                wiki_dir = kb / "wiki"
                if wiki_dir.is_dir():
                    scan_dirs.append(wiki_dir)

    for scan_dir in scan_dirs:
        if not scan_dir.is_dir():
            continue

        for md_file in scan_dir.rglob("*.md"):
            # Skip hidden dirs
            try:
                rel_parts = md_file.relative_to(scan_dir).parts
            except ValueError:
                rel_parts = md_file.parts
            if any(p.startswith(".") for p in rel_parts):
                continue

            stats["scanned"] += 1

            try:
                raw = md_file.read_text(encoding="utf-8")
                post = frontmatter.loads(raw)
            except Exception as e:
                log.debug("Cannot parse %s: %s", md_file.name, e)
                stats["errors"] += 1
                continue

            metadata = dict(post.metadata)
            changed = False

            # Skip files without any frontmatter
            if not metadata:
                stats["skipped"] += 1
                continue

            # Only process agent-authored files (or user journal)
            author = metadata.get("author", "")
            if not author:
                stats["skipped"] += 1
                continue

            # Add date: if missing
            if "date" not in metadata and "date_created" not in metadata:
                date_val = _extract_date_from_title(metadata.get("title", ""))
                if not date_val:
                    date_val = _extract_date_from_filename(md_file.name)
                if date_val:
                    post.metadata["date"] = date_val
                    changed = True

            # Add agent: if missing (only for agent-authored files)
            if author == "agent" and "agent" not in metadata:
                agent_name = _get_agent_for_file(md_file.name, metadata)
                if agent_name:
                    post.metadata["agent"] = agent_name
                    changed = True

            if changed:
                if not dry_run:
                    md_file.write_text(frontmatter.dumps(post), encoding="utf-8")
                stats["updated"] += 1
                if paths:
                    rel = paths.to_logical(md_file)
                else:
                    rel = str(md_file.relative_to(vault_dir))
                updated_files.append(rel)
                log.info("Updated: %s", rel)
            else:
                stats["skipped"] += 1

    return {**stats, "files": updated_files}
