"""HotMemory and VaultMemory classes — file-based memory tiers.

HotMemory: Tier 1 — computed view of top facts from the DB (+ MEMORY.md fallback).
VaultMemory: Tier 2 — structured vault (memory/) with append-only markdown.
"""

import logging
import re
from datetime import UTC, datetime
from pathlib import Path

from timmy.memory.crud import recall_last_reflection, recall_personal_facts
from timmy.memory.db import HOT_MEMORY_PATH, VAULT_PATH

logger = logging.getLogger(__name__)

# ── Default template ─────────────────────────────────────────────────────────

_DEFAULT_HOT_MEMORY_TEMPLATE = """\
# Timmy Hot Memory

> Working RAM — always loaded, ~300 lines max, pruned monthly
> Last updated: {date}

---

## Current Status

**Agent State:** Operational
**Mode:** Development
**Active Tasks:** 0
**Pending Decisions:** None

---

## Standing Rules

1. **Sovereignty First** — No cloud dependencies
2. **Local-Only Inference** — Ollama on localhost
3. **Privacy by Design** — Telemetry disabled
4. **Tool Minimalism** — Use tools only when necessary
5. **Memory Discipline** — Write handoffs at session end

---

## Agent Roster

| Agent | Role | Status |
|-------|------|--------|
| Timmy | Core | Active |

---

## User Profile

**Name:** (not set)
**Interests:** (to be learned)

---

## Key Decisions

(none yet)

---

## Pending Actions

- [ ] Learn user's name

---

*Prune date: {prune_date}*
"""


# ── HotMemory ────────────────────────────────────────────────────────────────


class HotMemory:
    """Tier 1: Hot memory — computed view of top facts from DB."""

    def __init__(self) -> None:
        self.path = HOT_MEMORY_PATH
        self._content: str | None = None
        self._last_modified: float | None = None

    def read(self, force_refresh: bool = False) -> str:
        """Read hot memory — computed view of top facts + last reflection from DB."""
        try:
            facts = recall_personal_facts()
            now = datetime.now(UTC).strftime("%Y-%m-%d %H:%M UTC")
            lines = [
                "# Timmy Hot Memory\n",
                "> Working RAM — always loaded, ~300 lines max, pruned monthly",
                f"> Last updated: {now}\n",
            ]

            if facts:
                lines.append("## Known Facts\n")
                for f in facts[:15]:
                    lines.append(f"- {f}")

            # Include the last reflection if available
            reflection = recall_last_reflection()
            if reflection:
                lines.append("\n## Last Reflection\n")
                lines.append(reflection)

            if len(lines) > 1:
                return "\n".join(lines)
        except Exception:
            logger.debug("DB context read failed, falling back to file")

        # Fallback to file if DB unavailable
        if self.path.exists():
            return self.path.read_text()

        return "# Timmy Hot Memory\n\nNo memories stored yet.\n"

    def update_section(self, section: str, content: str) -> None:
        """Update a specific section in MEMORY.md.

        DEPRECATED: Hot memory is now computed from the database.
        This method is kept for backward compatibility during transition.
        Use memory_write() to store facts in the database.
        """
        logger.warning(
            "HotMemory.update_section() is deprecated. "
            "Use memory_write() to store facts in the database."
        )

        # Keep file-writing for backward compatibility during transition
        # Guard against empty or excessively large writes
        if not content or not content.strip():
            logger.warning("HotMemory: Refusing empty write to section '%s'", section)
            return
        if len(content) > 2000:
            logger.warning("HotMemory: Truncating oversized write to section '%s'", section)
            content = content[:2000] + "\n... [truncated]"

        if not self.path.exists():
            self._create_default()

        full_content = self.read()

        # Find section
        pattern = rf"(## {re.escape(section)}.*?)(?=\n## |\Z)"
        match = re.search(pattern, full_content, re.DOTALL)

        if match:
            # Replace section
            new_section = f"## {section}\n\n{content}\n\n"
            full_content = full_content[: match.start()] + new_section + full_content[match.end() :]
        else:
            # Append section — guard against missing prune marker
            insert_point = full_content.rfind("*Prune date:")
            new_section = f"## {section}\n\n{content}\n\n"
            if insert_point < 0:
                # No prune marker — just append at end
                full_content = full_content.rstrip() + "\n\n" + new_section
            else:
                full_content = (
                    full_content[:insert_point] + new_section + "\n" + full_content[insert_point:]
                )

        self.path.write_text(full_content)
        self._content = full_content
        self._last_modified = self.path.stat().st_mtime
        logger.info("HotMemory: Updated section '%s'", section)

    def _create_default(self) -> None:
        """Create default MEMORY.md if missing.

        DEPRECATED: Hot memory is now computed from the database.
        This method is kept for backward compatibility during transition.
        """
        logger.debug(
            "HotMemory._create_default() - creating default MEMORY.md for backward compatibility"
        )
        now = datetime.now(UTC)
        content = _DEFAULT_HOT_MEMORY_TEMPLATE.format(
            date=now.strftime("%Y-%m-%d"),
            prune_date=now.replace(day=25).strftime("%Y-%m-%d"),
        )
        self.path.write_text(content)
        logger.info("HotMemory: Created default MEMORY.md")


# ── VaultMemory ──────────────────────────────────────────────────────────────


class VaultMemory:
    """Tier 2: Structured vault (memory/) — append-only markdown."""

    def __init__(self) -> None:
        self.path = VAULT_PATH
        self._ensure_structure()

    def _ensure_structure(self) -> None:
        """Ensure vault directory structure exists."""
        (self.path / "self").mkdir(parents=True, exist_ok=True)
        (self.path / "notes").mkdir(parents=True, exist_ok=True)
        (self.path / "aar").mkdir(parents=True, exist_ok=True)

    def write_note(self, name: str, content: str, namespace: str = "notes") -> Path:
        """Write a note to the vault."""
        # Add timestamp to filename
        timestamp = datetime.now(UTC).strftime("%Y%m%d")
        filename = f"{timestamp}_{name}.md"
        filepath = self.path / namespace / filename

        # Add header
        full_content = f"""# {name.replace("_", " ").title()}

> Created: {datetime.now(UTC).isoformat()}
> Namespace: {namespace}

---

{content}

---

*Auto-generated by Timmy Memory System*
"""

        filepath.write_text(full_content)
        logger.info("VaultMemory: Wrote %s", filepath)
        return filepath

    def read_file(self, filepath: Path) -> str:
        """Read a file from the vault."""
        if not filepath.exists():
            return ""
        return filepath.read_text()

    def update_user_profile(self, key: str, value: str) -> None:
        """Update a field in user_profile.md.

        DEPRECATED: User profile updates should now use memory_write() to store
        facts in the database. This method is kept for backward compatibility.
        """
        logger.warning(
            "VaultMemory.update_user_profile() is deprecated. "
            "Use memory_write() to store user facts in the database."
        )
        # Still update the file for backward compatibility during transition
        profile_path = self.path / "self" / "user_profile.md"

        if not profile_path.exists():
            self._create_default_profile()

        content = profile_path.read_text()

        pattern = rf"(\*\*{re.escape(key)}:\*\*).*"
        if re.search(pattern, content):
            safe_value = value.strip()
            content = re.sub(pattern, lambda m: f"{m.group(1)} {safe_value}", content)
        else:
            facts_section = "## Important Facts"
            if facts_section in content:
                insert_point = content.find(facts_section) + len(facts_section)
                content = content[:insert_point] + f"\n- {key}: {value}" + content[insert_point:]

        content = re.sub(
            r"\*Last updated:.*\*",
            f"*Last updated: {datetime.now(UTC).strftime('%Y-%m-%d')}*",
            content,
        )

        profile_path.write_text(content)
        logger.info("VaultMemory: Updated user profile: %s = %s", key, value)

    def _create_default_profile(self) -> None:
        """Create default user profile."""
        profile_path = self.path / "self" / "user_profile.md"
        default = """# User Profile

> Learned information about the user.

## Basic Information

**Name:** (unknown)
**Location:** (unknown)
**Occupation:** (unknown)

## Interests & Expertise

- (to be learned)

## Preferences

- Response style: concise, technical
- Tool usage: minimal

## Important Facts

- (to be extracted)

---

*Last updated: {date}*
""".format(date=datetime.now(UTC).strftime("%Y-%m-%d"))

        profile_path.write_text(default)
