"""Export memories as AI context files for different IDE / agent tools."""
from __future__ import annotations

import os
import tempfile
from collections import defaultdict
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from .config import load_config
from .store import list_memories

_HEADER = (
    "> Auto-generated by `lore export` on {date}. "
    "Do not edit manually — use `lore add` to update.\n"
)

_OWASP_ITEMS = [
    "Broken access control",
    "Cryptographic failures",
    "Injection attacks (SQL, XSS, command injection)",
    "Insecure design",
    "Security misconfiguration",
    "Vulnerable and outdated components",
    "Identification and authentication failures",
    "Software and data integrity failures",
    "Security logging and monitoring failures",
    "Server-side request forgery (SSRF)",
]


def _render_security_preamble(config: dict) -> str:
    """Build a Markdown security guidelines section from config."""
    sec = config.get("security", {})
    if not sec.get("enabled", False):
        return ""

    lines: list[str] = [
        "## Security Guidelines\n",
        "> The following rules apply to **all AI-generated code** in this repository.\n",
    ]

    lines.append(
        "- **Never generate**: hardcoded credentials, disabled SSL/TLS verification, "
        "permissive CORS (`*`), `eval()` on user input, or shell-injection patterns."
    )

    if sec.get("owasp_top10", True):
        owasp = ", ".join(_OWASP_ITEMS)
        lines.append(
            f"- **Follow OWASP Top 10** — prevent: {owasp}."
        )

    policy = sec.get("security_policy", "SECURITY.md")
    if policy:
        lines.append(
            f"- Review [`{policy}`]({policy}) for this project's vulnerability "
            "disclosure policy before suggesting security-related changes."
        )

    if sec.get("codeowners", True):
        lines.append(
            "- Sensitive paths are governed by [`CODEOWNERS`](CODEOWNERS) — "
            "changes to those files require additional human review."
        )

    for rule in sec.get("custom_rules", []):
        if rule.strip():
            lines.append(f"- {rule.strip()}")

    lines.append("")
    return "\n".join(lines) + "\n"


def _render_memories_markdown(root: Path) -> str:
    """Return all memories formatted as grouped Markdown."""
    memories = list_memories(root)
    if not memories:
        return "_No memories stored yet._\n"

    by_cat: dict[str, list] = defaultdict(list)
    for m in memories:
        by_cat[m.get("category", "facts")].append(m)

    lines: list[str] = []
    for cat in sorted(by_cat):
        lines.append(f"## {cat.capitalize()}\n")
        for e in by_cat[cat]:
            content = e.get("content", "")
            tags = e.get("tags", [])
            tag_str = f" _{', '.join(tags)}_" if tags else ""
            lines.append(f"- {content}{tag_str}")
        lines.append("")
    return "\n".join(lines)


def _datestamp() -> str:
    return datetime.now(timezone.utc).strftime("%Y-%m-%d")


def _atomic_write(dest: Path, content: str) -> None:
    """Write *content* to *dest* atomically via a temp file + rename.

    Prevents partial/stale output if the process is interrupted mid-export.
    """
    dest.parent.mkdir(parents=True, exist_ok=True)
    fd, tmp = tempfile.mkstemp(dir=dest.parent, suffix=".tmp")
    try:
        with os.fdopen(fd, "w") as f:
            f.write(content)
        os.replace(tmp, dest)
    except Exception:
        try:
            os.unlink(tmp)
        except OSError:
            pass
        raise


def export_agents_md(root: Path, out: Path | None = None) -> Path:
    config = load_config(root)
    body = _render_memories_markdown(root)
    security = _render_security_preamble(config)
    dest = out or (root / "AGENTS.md")
    _atomic_write(
        dest,
        "# Agent Memory\n\n"
        + _HEADER.format(date=_datestamp())
        + (f"\n{security}" if security else "")
        + f"\n{body}"
    )
    return dest


def export_copilot(root: Path, out: Path | None = None) -> Path:
    config = load_config(root)
    body = _render_memories_markdown(root)
    security = _render_security_preamble(config)
    dest = out or (root / ".github" / "copilot-instructions.md")
    _atomic_write(
        dest,
        "<!-- Auto-generated by `lore export`. Do not edit manually. -->\n\n"
        "# Copilot Instructions\n\n"
        + _HEADER.format(date=_datestamp())
        + (f"\n{security}" if security else "")
        + f"\n{body}"
    )
    return dest


def export_cursor(root: Path, out: Path | None = None) -> Path:
    config = load_config(root)
    body = _render_memories_markdown(root)
    security = _render_security_preamble(config)
    dest = out or (root / ".cursor" / "rules" / "memory.md")
    _atomic_write(
        dest,
        "# Project Memory\n\n"
        + _HEADER.format(date=_datestamp())
        + (f"\n{security}" if security else "")
        + f"\n{body}"
    )
    return dest


def export_claude(root: Path, out: Path | None = None) -> Path:
    config = load_config(root)
    body = _render_memories_markdown(root)
    security = _render_security_preamble(config)
    dest = out or (root / "CLAUDE.md")
    _atomic_write(
        dest,
        "# Claude Memory\n\n"
        + _HEADER.format(date=_datestamp())
        + (f"\n{security}" if security else "")
        + f"\n{body}"
    )
    return dest


EXPORT_TARGETS: dict[str, Any] = {
    "agents": export_agents_md,
    "copilot": export_copilot,
    "cursor": export_cursor,
    "claude": export_claude,
}


def export_all(root: Path) -> list[Path]:
    """Export to all targets enabled in .memory/config.yaml."""
    config = load_config(root)
    enabled = config.get("export_targets", {})
    written: list[Path] = []
    for name, fn in EXPORT_TARGETS.items():
        if enabled.get(name, True):
            written.append(fn(root))
    return written
