"""Context loading and project state management.

Provides functions to:
- Load context files based on manifest.json loading_strategy
- Read and update project/status.md
- List .agent/ files with metadata
"""

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


def get_context_files(
    agent_dir: Path,
    stack: str | None = None,
    task: str | None = None,
) -> list[str]:
    """
    Get context files based on stack and task type.

    Uses manifest.json loading_strategy to determine which files to load.
    """
    manifest_path = agent_dir / "manifest.json"
    if not manifest_path.exists():
        return []

    try:
        manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError):
        return []

    loading_strategy = manifest.get("loading_strategy", {})
    layers = loading_strategy.get("layers", {})
    task_profiles = loading_strategy.get("task_profiles", {})

    files: list[str] = []

    essential = layers.get("essential", {})
    files.extend(essential.get("files", []))

    context = layers.get("context", {})
    files.extend(context.get("files", []))

    if stack:
        stack_specs = layers.get("stack_specs", {})
        options = stack_specs.get("options", {})
        if stack in options:
            files.extend(options[stack])

    if task:
        if task in task_profiles:
            profile = task_profiles[task]

            workflows = layers.get("workflows", {})
            mappings = workflows.get("mappings", {})
            for workflow in profile.get("workflows", []):
                if workflow in mappings:
                    files.extend(mappings[workflow])

            skills = layers.get("skills", {})
            modules = skills.get("modules", {})
            for skill in profile.get("skills", []):
                if skill in modules:
                    module = modules[skill]
                    if "entry" in module:
                        files.append(module["entry"])
                    files.extend(module.get("files", []))
        else:
            workflows = layers.get("workflows", {})
            mappings = workflows.get("mappings", {})
            if task in mappings:
                files.extend(mappings[task])

    seen: set[str] = set()
    unique_files: list[str] = []
    for f in files:
        if f not in seen:
            seen.add(f)
            unique_files.append(f)

    return unique_files


def build_context_content(
    agent_dir: Path,
    file_list: list[str],
    encoding: str = "utf-8",
) -> dict[str, str]:
    """
    Read each file in file_list under agent_dir and return path -> content.

    Missing files are omitted.
    """
    result: dict[str, str] = {}
    for rel_path in file_list:
        full_path = agent_dir / rel_path
        if full_path.exists() and full_path.is_file():
            try:
                result[rel_path] = full_path.read_text(encoding=encoding)
            except OSError:
                result[rel_path] = ""
    return result


def read_status(agent_dir: Path) -> str:
    """Read project/status.md and return its content."""
    status_path = agent_dir / "project" / "status.md"
    if not status_path.exists():
        return ""
    try:
        return status_path.read_text(encoding="utf-8")
    except OSError:
        return ""


def update_status_section(
    agent_dir: Path,
    *,
    completed_tasks: list[str] | None = None,
    new_tasks: list[str] | None = None,
    blockers: list[str] | None = None,
    session_context: str | None = None,
) -> str:
    """
    Structured update to project/status.md.

    Modifies specific sections without rewriting the entire file.
    Returns the updated content or an error message.
    """
    status_path = agent_dir / "project" / "status.md"
    if not status_path.exists():
        return "Error: project/status.md not found"

    try:
        content = status_path.read_text(encoding="utf-8")
    except OSError as e:
        return f"Error reading status.md: {e}"

    now = datetime.now(timezone.utc).strftime("%Y-%m-%d")

    content = re.sub(
        r"(> Last updated:) .+",
        rf"\1 {now} | Updated by: MCP checkpoint",
        content,
        count=1,
    )

    if completed_tasks:
        for task in completed_tasks:
            pattern = re.compile(
                r"^(- \[[ ]\] " + re.escape(task) + r")$",
                re.MULTILINE,
            )
            content = pattern.sub(f"- [x] {task}", content, count=1)

            if not pattern.search(content):
                recently = "### Recently Completed"
                if recently in content:
                    content = content.replace(
                        recently,
                        f"{recently}\n- [x] {task}",
                    )

    if new_tasks:
        pending_header = "### Pending"
        if pending_header in content:
            new_items = "\n".join(f"- [ ] {t}" for t in new_tasks)
            content = content.replace(
                pending_header,
                f"{pending_header}\n{new_items}",
            )

    if blockers is not None:
        blockers_match = re.search(
            r"(## Blockers\n\n)(.*?)(\n\n## |\n---|\Z)",
            content,
            re.DOTALL,
        )
        if blockers_match:
            if blockers:
                blocker_text = "\n".join(f"- {b}" for b in blockers)
            else:
                blocker_text = "None."
            content = (
                content[: blockers_match.start(2)]
                + blocker_text
                + content[blockers_match.end(2) :]
            )

    if session_context:
        ctx_match = re.search(
            r"(## Session Context\n\n)(.*?)(\n\n## |\n---|\Z)",
            content,
            re.DOTALL,
        )
        if ctx_match:
            content = (
                content[: ctx_match.start(2)]
                + session_context
                + content[ctx_match.end(2) :]
            )

    try:
        status_path.write_text(content, encoding="utf-8")
    except OSError as e:
        return f"Error writing status.md: {e}"

    return content


def list_project_files(agent_dir: Path) -> list[dict[str, str]]:
    """
    List all .agent/ files with metadata (layer, description).

    Returns a list of dicts with keys: path, layer, description.
    """
    _LAYER_MAP = {
        "project/status.md": ("essential", "Current development state"),
        "start-here.md": ("essential", "Protocol entry point"),
        "core/core-rules.md": ("essential", "Non-negotiable core principles"),
        "core/instructions.md": ("context", "AI collaboration guidelines"),
        "project/context.md": ("context", "Project business context"),
        "project/tech-stack.md": ("context", "Technology stack decisions"),
        "project/known-issues.md": ("context", "Known issues and workarounds"),
        "project/deploy.md": ("context", "Deployment environments and infrastructure"),
        "project/commands.md": ("context", "Common project commands"),
        "project/conventions.md": ("reference", "Project-specific conventions"),
        "project/session-journal.md": ("on-demand", "Session history log"),
        "core/conventions.md": ("reference", "Naming and Git conventions"),
        "core/security.md": ("reference", "Security rules"),
        "core/examples.md": ("reference", "Code examples"),
        "manifest.json": ("internal", "Protocol configuration and loading strategy"),
    }

    result: list[dict[str, str]] = []

    if not agent_dir.exists():
        return result

    for file_path in sorted(agent_dir.rglob("*")):
        if not file_path.is_file():
            continue
        rel = file_path.relative_to(agent_dir).as_posix()

        if rel in _LAYER_MAP:
            layer, desc = _LAYER_MAP[rel]
        elif rel.startswith("core/stack-specs/"):
            layer, desc = "stack-specific", f"Stack spec: {file_path.stem}"
        elif rel.startswith("core/workflows/"):
            layer, desc = "workflow", f"Workflow: {file_path.stem}"
        elif rel.startswith("skills/"):
            layer, desc = "skill", f"Skill: {file_path.stem}"
        elif rel.startswith("project/adr/"):
            layer, desc = "on-demand", f"Architecture decision: {file_path.stem}"
        elif rel.startswith("meta/"):
            layer, desc = "on-demand", f"Protocol meta: {file_path.stem}"
        elif rel.startswith("adapters/"):
            layer, desc = "internal", f"Adapter template: {file_path.stem}"
        elif rel.startswith("templates/"):
            layer, desc = "internal", f"Init template: {file_path.stem}"
        elif rel.startswith("scripts/"):
            layer, desc = "internal", f"Script: {file_path.stem}"
        elif rel.startswith("rules/"):
            layer, desc = "internal", f"Legacy rule: {file_path.stem}"
        else:
            layer, desc = "other", file_path.stem

        result.append({"path": rel, "layer": layer, "description": desc})

    return result
