"""
Operational Memory - Authoritative state management.

This module provides:
- Project state reading/writing
- Task history tracking
- Decision management
- Constraint enforcement

Operational memory is the "source of truth" that agents read first.

Usage:
    from agent_orchestrator.memory.operational import OperationalMemory

    om = OperationalMemory(db, ops_path)
    state = om.read_project_state()
    constraints = om.get_constraints()
"""

import json
import logging
from datetime import datetime
from pathlib import Path
from typing import Any, Optional

from ..persistence.database import OrchestratorDB
from ..persistence.models import Task


logger = logging.getLogger(__name__)


class OperationalMemory:
    """
    Operational Memory - Authoritative system state.

    This is the deterministic memory layer that the control loop
    reads first. It provides:
    - Current project state and constraints
    - Task history and outcomes
    - Approval records
    - Agent capabilities registry
    """

    def __init__(self, db: OrchestratorDB, ops_path: Path):
        """
        Initialize Operational Memory.

        Args:
            db: Database for task/approval history
            ops_path: Path to /ops/ directory
        """
        self.db = db
        self.ops_path = Path(ops_path)
        self._state_file = self.ops_path / "project_state.json"

    def read_project_state(self) -> dict[str, Any]:
        """
        Read the authoritative project state.

        Returns:
            Project state dictionary
        """
        if not self._state_file.exists():
            return self._create_default_state()

        try:
            content = self._state_file.read_text()
            return json.loads(content)
        except json.JSONDecodeError as e:
            logger.error(f"Failed to parse project state: {e}")
            return self._create_default_state()

    def _create_default_state(self) -> dict[str, Any]:
        """Create a default project state."""
        return {
            "version": "1.0.0",
            "project": {
                "name": "Unknown",
                "description": "",
            },
            "current_phase": "Unknown",
            "objectives": [],
            "constraints": [],
            "decisions": [],
            "active_agents": [],
            "last_updated": datetime.now().isoformat(),
            "updated_by": "system",
        }

    def write_project_state(self, state: dict[str, Any]) -> None:
        """
        Write project state to file.

        Note: Should go through Memory Write Gate for validation.

        Args:
            state: New state to write
        """
        state["last_updated"] = datetime.now().isoformat()

        self.ops_path.mkdir(parents=True, exist_ok=True)
        self._state_file.write_text(json.dumps(state, indent=2))

        logger.info("Project state updated")

    def get_constraints(self) -> list[str]:
        """Get active constraints from project state."""
        state = self.read_project_state()
        return state.get("constraints", [])

    def get_active_objectives(self) -> list[dict[str, Any]]:
        """Get active (non-completed) objectives."""
        state = self.read_project_state()
        objectives = state.get("objectives", [])
        return [o for o in objectives if o.get("status") != "completed"]

    def get_active_decisions(self) -> list[dict[str, Any]]:
        """Get recent decisions (last 10)."""
        state = self.read_project_state()
        decisions = state.get("decisions", [])
        return decisions[-10:]  # Last 10 decisions

    def record_decision(self, decision: str, rationale: str, adr_ref: Optional[str] = None) -> None:
        """
        Record a new decision in project state.

        Args:
            decision: The decision made
            rationale: Why this decision was made
            adr_ref: Reference to ADR document if applicable
        """
        state = self.read_project_state()

        new_decision = {
            "id": f"dec-{len(state.get('decisions', [])) + 1:03d}",
            "date": datetime.now().strftime("%Y-%m-%d"),
            "decision": decision,
            "rationale": rationale,
        }
        if adr_ref:
            new_decision["adr"] = adr_ref

        if "decisions" not in state:
            state["decisions"] = []
        state["decisions"].append(new_decision)

        self.write_project_state(state)

    def get_task_history(
        self,
        agent_id: Optional[str] = None,
        status: Optional[str] = None,
        limit: int = 20,
    ) -> list[Task]:
        """
        Get task history from database.

        Args:
            agent_id: Filter by agent
            status: Filter by status
            limit: Maximum results

        Returns:
            List of Task objects
        """
        # Query database
        tasks = self.db.get_recent_tasks(limit=limit)

        if agent_id:
            tasks = [t for t in tasks if t.assigned_to == agent_id]

        if status:
            tasks = [t for t in tasks if t.status == status]

        return tasks

    def record_task_outcome(
        self,
        task_id: str,
        outcome: str,
        artifacts: list[str],
        next_steps: list[str],
    ) -> None:
        """
        Record the outcome of a completed task.

        Args:
            task_id: ID of the task
            outcome: Summary of what happened
            artifacts: List of artifact paths/IDs
            next_steps: Recommended next steps
        """
        # Update task in database
        self.db.update_task_status(
            task_id=task_id,
            status="completed",
            result=outcome,
        )

        logger.info(f"Recorded outcome for task {task_id}")

    def get_agent_capabilities(self, agent_id: str) -> dict[str, Any]:
        """
        Get capabilities for an agent.

        Args:
            agent_id: The agent ID

        Returns:
            Capability dictionary
        """
        agent = self.db.get_agent(agent_id)

        if not agent:
            return {}

        return {
            "agent_id": agent.agent_id,
            "agent_type": agent.agent_type,
            "risk_tier_max": agent.risk_tier_max,
            "tools_allowed": agent.tools_allowed,
            "tools_blocked": agent.tools_blocked,
            "is_active": agent.is_active,
        }

    def get_all_agent_capabilities(self) -> list[dict[str, Any]]:
        """Get capabilities for all registered agents."""
        agents = self.db.get_all_agents()
        return [self.get_agent_capabilities(a.agent_id) for a in agents]

    def get_pending_approvals(self) -> list[dict[str, Any]]:
        """Get pending approval requests."""
        approvals = self.db.get_pending_approvals()

        return [
            {
                "id": a.id,
                "run_id": a.run_id,
                "agent_id": a.agent_id,
                "action_type": a.action_type,
                "target": a.target,
                "risk_level": a.risk_level,
                "requested_at": a.requested_at.isoformat() if a.requested_at else None,
            }
            for a in approvals
        ]

    def build_context_for_agent(self, agent_id: str, task_type: str) -> dict[str, Any]:
        """
        Build operational context to inject into an agent's prompt.

        Args:
            agent_id: The agent being invoked
            task_type: Type of task being performed

        Returns:
            Context dictionary for prompt injection
        """
        state = self.read_project_state()
        capabilities = self.get_agent_capabilities(agent_id)

        return {
            "project_state": {
                "phase": state.get("current_phase"),
                "version": state.get("version"),
            },
            "constraints": self.get_constraints(),
            "recent_decisions": [d["decision"] for d in self.get_active_decisions()],
            "active_objectives": self.get_active_objectives(),
            "agent_capabilities": capabilities,
            "pending_approvals": len(self.get_pending_approvals()),
        }

    def get_failures_and_fixes(self, error_pattern: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get recorded failures and their fixes.

        Args:
            error_pattern: Optional pattern to filter by

        Returns:
            List of failure/fix records
        """
        # This would query a failures table or the patterns file
        # For now, return empty - will be populated by Memory Librarian
        return []

    def get_current_phase(self) -> str:
        """Get the current project phase."""
        state = self.read_project_state()
        return state.get("current_phase", "Unknown")

    def update_current_phase(self, phase: str) -> None:
        """Update the current project phase."""
        state = self.read_project_state()
        state["current_phase"] = phase
        self.write_project_state(state)
