"""
Archive Operations Mixin for ContextStore.

Provides archive and restore functionality:
- get_memory_with_vector: Get memory including embedding
- archive_memory: Archive a memory before deletion
- delete_memory: Delete with optional archival
- list_archived: List archived memories
- get_archive_stats: Archive statistics
- restore_memory: Restore from archive
- permanently_delete_archived: Hard delete from archive
- update_memory: Update memory fields
"""

from typing import Dict, List, Optional

from ..utils.timestamps import utc_now_iso, utc_timestamp_ms
from .utils import parse_files


class ArchiveMixin:
    """
    Mixin class providing archive management operations.

    Requires the base class to have:
    - self.db: LanceDB connection
    - self.table: LanceDB memories table (or None)
    - self.archive_table: LanceDB archive table (or None)
    - self.archive_table_name: str
    - self.project_name: str
    """

    def get_memory_with_vector(self, memory_id: str) -> Optional[Dict]:
        """Get a memory including its vector embedding for archiving."""
        if self.table is None:
            return None
        try:
            df = self.table.to_pandas()
            match = df[df['id'] == memory_id]
            if match.empty:
                return None
            r = match.iloc[0]
            return {
                "id": r.get("id"),
                "type": r.get("type"),
                "title": r.get("title"),
                "fact": r.get("fact"),
                "files": r.get("files", "[]"),
                "project": r.get("project", self.project_name),
                "created_at": r.get("created_at", ""),
                "vector": r.get("vector", [])
            }
        except Exception as e:
            print(f"[Store:{self.project_name}] Error getting memory with vector: {e}")
            return None

    def archive_memory(
        self,
        memory: Dict,
        deletion_reason: str,
        deletion_source: str,
        replaced_by_id: str = None,
        conflict_id: str = None,
        cluster_id: str = None,
        reasoning: str = None
    ) -> bool:
        """
        Archive a memory before deletion.

        Args:
            memory: The memory dict including vector field
            deletion_reason: "conflict_resolution" | "consolidation" | "manual"
            deletion_source: "auto_resolve" | "llm_resolve" | "user" | "consolidation_llm"
            replaced_by_id: ID of the keeper/merged memory
            conflict_id: If from conflict resolution
            cluster_id: If from consolidation
            reasoning: LLM reasoning or auto-rule description

        Returns:
            True if archived successfully
        """
        record = {
            "id": memory["id"],
            "type": memory.get("type", ""),
            "title": memory.get("title", ""),
            "fact": memory.get("fact", ""),
            "files": memory.get("files", "[]"),
            "project": self.project_name,
            "created_at": memory.get("created_at", ""),
            "vector": memory.get("vector", []),
            "deleted_at": utc_now_iso(),
            "deletion_reason": deletion_reason,
            "deletion_source": deletion_source,
            "replaced_by_id": replaced_by_id or "",
            "conflict_id": conflict_id or "",
            "cluster_id": cluster_id or "",
            "reasoning": reasoning or ""
        }

        try:
            if self.archive_table is None:
                self.archive_table = self.db.create_table(self.archive_table_name, [record])
            else:
                self.archive_table.add([record])
            print(f"[Store:{self.project_name}] Archived memory {memory['id']}")
            return True
        except Exception as e:
            print(f"[Store:{self.project_name}] Error archiving: {e}")
            return False

    def delete_memory(
        self,
        memory_id: str,
        archive: bool = True,
        deletion_reason: str = "manual",
        deletion_source: str = "user",
        replaced_by_id: str = None,
        conflict_id: str = None,
        cluster_id: str = None,
        reasoning: str = None
    ) -> bool:
        """
        Delete a memory, optionally archiving it first.

        Args:
            memory_id: ID of the memory to delete
            archive: If True, archive before deleting (default True)
            deletion_reason: "conflict_resolution" | "consolidation" | "manual"
            deletion_source: "auto_resolve" | "llm_resolve" | "user" | "consolidation_llm"
            replaced_by_id: ID of the keeper/merged memory (for tracking)
            conflict_id: If from conflict resolution
            cluster_id: If from consolidation
            reasoning: LLM reasoning or auto-rule description

        Returns:
            True if deleted successfully
        """
        if self.table is None:
            return False

        try:
            if archive:
                memory = self.get_memory_with_vector(memory_id)
                if memory:
                    self.archive_memory(
                        memory=memory,
                        deletion_reason=deletion_reason,
                        deletion_source=deletion_source,
                        replaced_by_id=replaced_by_id,
                        conflict_id=conflict_id,
                        cluster_id=cluster_id,
                        reasoning=reasoning
                    )

            self.table.delete(f'id = "{memory_id}"')
            return True
        except Exception as e:
            print(f"[Store:{self.project_name}] Error deleting memory: {e}")
            return False

    def list_archived(
        self,
        limit: int = 50,
        offset: int = 0,
        reason_filter: Optional[str] = None
    ) -> List[Dict]:
        """
        List archived memories with optional filtering.

        Args:
            limit: Maximum number of entries to return
            offset: Number of entries to skip
            reason_filter: Filter by deletion_reason (e.g., "conflict_resolution")

        Returns:
            List of archived memory records
        """
        if self.archive_table is None:
            return []
        try:
            raw_results = self.archive_table.to_pandas().to_dict('records')
            if reason_filter:
                raw_results = [r for r in raw_results if r.get("deletion_reason") == reason_filter]
            raw_results.sort(key=lambda x: x.get("deleted_at", ""), reverse=True)
            # Map to JSON-serializable format (exclude vector field)
            return [
                {
                    "id": r.get("id"),
                    "type": r.get("type", ""),
                    "title": r.get("title", ""),
                    "fact": r.get("fact", ""),
                    "files": parse_files(r.get("files")),
                    "project": r.get("project", self.project_name),
                    "created_at": r.get("created_at", ""),
                    "deleted_at": r.get("deleted_at", ""),
                    "deletion_reason": r.get("deletion_reason", ""),
                    "deletion_source": r.get("deletion_source", ""),
                    "replaced_by_id": r.get("replaced_by_id", ""),
                    "conflict_id": r.get("conflict_id", ""),
                    "cluster_id": r.get("cluster_id", ""),
                    "reasoning": r.get("reasoning", ""),
                }
                for r in raw_results[offset:offset + limit]
            ]
        except Exception as e:
            print(f"[Store:{self.project_name}] Error listing archive: {e}")
            return []

    def get_archive_stats(self) -> Dict:
        """
        Get archive statistics.

        Returns:
            Dict with total count and breakdown by deletion reason
        """
        if self.archive_table is None:
            return {"total": 0, "by_reason": {}}
        try:
            results = self.archive_table.to_pandas().to_dict('records')
            by_reason = {}
            for r in results:
                reason = r.get("deletion_reason", "unknown")
                by_reason[reason] = by_reason.get(reason, 0) + 1
            return {"total": len(results), "by_reason": by_reason}
        except Exception:
            return {"total": 0, "by_reason": {}}

    def restore_memory(self, archived_id: str) -> Dict:
        """
        Restore an archived memory back to the main table.

        Creates a new memory with _restored_ suffix in ID.

        Args:
            archived_id: The original memory ID in the archive

        Returns:
            Dict with success status and restored memory ID
        """
        if self.archive_table is None:
            return {"success": False, "error": "No archive table"}
        try:
            df = self.archive_table.to_pandas()
            match = df[df['id'] == archived_id]
            if match.empty:
                return {"success": False, "error": "Not found in archive"}

            archived = match.iloc[0].to_dict()
            restored_id = f"{archived['id']}_restored_{utc_timestamp_ms()}"

            record = {
                "id": restored_id,
                "type": archived["type"],
                "title": archived["title"],
                "fact": archived["fact"],
                "files": archived["files"],
                "project": self.project_name,
                "created_at": utc_now_iso(),
                "text": f"[{archived['type']}] {archived['title']}\n{archived['fact']}",
                "vector": archived["vector"]
            }
            self.table.add([record])
            self.archive_table.delete(f'id = "{archived_id}"')

            print(f"[Store:{self.project_name}] Restored memory {archived_id} as {restored_id}")
            return {"success": True, "restored_id": restored_id, "original_id": archived_id}
        except Exception as e:
            print(f"[Store:{self.project_name}] Error restoring memory: {e}")
            return {"success": False, "error": str(e)}

    def permanently_delete_archived(self, archived_id: str) -> bool:
        """
        Permanently delete from archive (no recovery).

        Args:
            archived_id: The memory ID in the archive

        Returns:
            True if deleted successfully
        """
        if self.archive_table is None:
            return False
        try:
            self.archive_table.delete(f'id = "{archived_id}"')
            print(f"[Store:{self.project_name}] Permanently deleted archived memory {archived_id}")
            return True
        except Exception as e:
            print(f"[Store:{self.project_name}] Error permanently deleting: {e}")
            return False

    def update_memory(self, memory_id: str, updates: Dict) -> bool:
        """
        Update fields on a memory.

        Note: LanceDB doesn't support direct updates, so we delete and re-add.
        """
        if self.table is None:
            return False

        try:
            # Get the current record
            df = self.table.to_pandas()
            match = df[df["id"] == memory_id]
            if match.empty:
                return False

            record = match.iloc[0].to_dict()

            # Apply updates
            for key, value in updates.items():
                if key in record:
                    record[key] = value

            # Delete old record
            self.table.delete(f'id = "{memory_id}"')

            # Re-add with updates
            self.table.add([record])
            return True
        except Exception as e:
            print(f"[Store:{self.project_name}] Error updating memory: {e}")
            return False
