"""Ticket ID persistence and recovery system."""

import json
import sqlite3
import threading
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any, Dict, List, Optional

from .queue import QueueItem, QueueStatus


class TicketRegistry:
    """Persistent registry for tracking ticket IDs and their lifecycle."""
    
    def __init__(self, db_path: Optional[Path] = None):
        """Initialize ticket registry.
        
        Args:
            db_path: Path to SQLite database. Defaults to ~/.mcp-ticketer/tickets.db
        """
        if db_path is None:
            db_dir = Path.home() / ".mcp-ticketer"
            db_dir.mkdir(parents=True, exist_ok=True)
            db_path = db_dir / "tickets.db"
        
        self.db_path = str(db_path)
        self._lock = threading.Lock()
        self._init_database()
    
    def _init_database(self):
        """Initialize database schema."""
        with sqlite3.connect(self.db_path) as conn:
            # Ticket registry table
            conn.execute("""
                CREATE TABLE IF NOT EXISTS ticket_registry (
                    queue_id TEXT PRIMARY KEY,
                    ticket_id TEXT,
                    adapter TEXT NOT NULL,
                    operation TEXT NOT NULL,
                    title TEXT,
                    status TEXT NOT NULL,
                    created_at TEXT NOT NULL,
                    updated_at TEXT NOT NULL,
                    ticket_data TEXT,
                    result_data TEXT,
                    error_message TEXT,
                    retry_count INTEGER DEFAULT 0,
                    CHECK (status IN ('queued', 'processing', 'completed', 'failed', 'recovered'))
                )
            """)
            
            # Create indices
            conn.execute("""
                CREATE INDEX IF NOT EXISTS idx_ticket_registry_ticket_id
                ON ticket_registry(ticket_id)
            """)
            conn.execute("""
                CREATE INDEX IF NOT EXISTS idx_ticket_registry_status
                ON ticket_registry(status)
            """)
            conn.execute("""
                CREATE INDEX IF NOT EXISTS idx_ticket_registry_adapter
                ON ticket_registry(adapter)
            """)
            
            # Ticket recovery log table
            conn.execute("""
                CREATE TABLE IF NOT EXISTS recovery_log (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    queue_id TEXT NOT NULL,
                    recovery_type TEXT NOT NULL,
                    recovery_data TEXT,
                    timestamp TEXT NOT NULL,
                    success BOOLEAN NOT NULL
                )
            """)
    
    def register_ticket_operation(
        self,
        queue_id: str,
        adapter: str,
        operation: str,
        title: str,
        ticket_data: Dict[str, Any]
    ) -> None:
        """Register a new ticket operation.
        
        Args:
            queue_id: Queue operation ID
            adapter: Adapter name
            operation: Operation type (create, update, etc.)
            title: Ticket title
            ticket_data: Original ticket data
        """
        with self._lock:
            with sqlite3.connect(self.db_path) as conn:
                conn.execute("""
                    INSERT OR REPLACE INTO ticket_registry (
                        queue_id, adapter, operation, title, status,
                        created_at, updated_at, ticket_data, retry_count
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                """, (
                    queue_id,
                    adapter,
                    operation,
                    title,
                    "queued",
                    datetime.now().isoformat(),
                    datetime.now().isoformat(),
                    json.dumps(ticket_data),
                    0
                ))
                conn.commit()
    
    def update_ticket_status(
        self,
        queue_id: str,
        status: str,
        ticket_id: Optional[str] = None,
        result_data: Optional[Dict[str, Any]] = None,
        error_message: Optional[str] = None,
        retry_count: Optional[int] = None
    ) -> None:
        """Update ticket operation status.
        
        Args:
            queue_id: Queue operation ID
            status: New status
            ticket_id: Created ticket ID (if available)
            result_data: Operation result data
            error_message: Error message if failed
            retry_count: Current retry count
        """
        with self._lock:
            with sqlite3.connect(self.db_path) as conn:
                update_fields = ["status = ?", "updated_at = ?"]
                values = [status, datetime.now().isoformat()]
                
                if ticket_id is not None:
                    update_fields.append("ticket_id = ?")
                    values.append(ticket_id)
                
                if result_data is not None:
                    update_fields.append("result_data = ?")
                    values.append(json.dumps(result_data))
                
                if error_message is not None:
                    update_fields.append("error_message = ?")
                    values.append(error_message)
                
                if retry_count is not None:
                    update_fields.append("retry_count = ?")
                    values.append(retry_count)
                
                values.append(queue_id)
                
                conn.execute(f"""
                    UPDATE ticket_registry
                    SET {', '.join(update_fields)}
                    WHERE queue_id = ?
                """, values)
                conn.commit()
    
    def get_ticket_info(self, queue_id: str) -> Optional[Dict[str, Any]]:
        """Get ticket information by queue ID.
        
        Args:
            queue_id: Queue operation ID
            
        Returns:
            Ticket information or None if not found
        """
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute("""
                SELECT * FROM ticket_registry WHERE queue_id = ?
            """, (queue_id,))
            
            row = cursor.fetchone()
            if not row:
                return None
            
            columns = [desc[0] for desc in cursor.description]
            ticket_info = dict(zip(columns, row))
            
            # Parse JSON fields
            if ticket_info.get("ticket_data"):
                ticket_info["ticket_data"] = json.loads(ticket_info["ticket_data"])
            if ticket_info.get("result_data"):
                ticket_info["result_data"] = json.loads(ticket_info["result_data"])
            
            return ticket_info
    
    def find_tickets_by_id(self, ticket_id: str) -> List[Dict[str, Any]]:
        """Find all operations for a specific ticket ID.
        
        Args:
            ticket_id: Ticket ID to search for
            
        Returns:
            List of ticket operations
        """
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute("""
                SELECT * FROM ticket_registry 
                WHERE ticket_id = ?
                ORDER BY created_at DESC
            """, (ticket_id,))
            
            results = []
            columns = [desc[0] for desc in cursor.description]
            
            for row in cursor.fetchall():
                ticket_info = dict(zip(columns, row))
                
                # Parse JSON fields
                if ticket_info.get("ticket_data"):
                    ticket_info["ticket_data"] = json.loads(ticket_info["ticket_data"])
                if ticket_info.get("result_data"):
                    ticket_info["result_data"] = json.loads(ticket_info["result_data"])
                
                results.append(ticket_info)
            
            return results
    
    def get_failed_operations(self, limit: int = 50) -> List[Dict[str, Any]]:
        """Get failed operations that might need recovery.
        
        Args:
            limit: Maximum number of operations to return
            
        Returns:
            List of failed operations
        """
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute("""
                SELECT * FROM ticket_registry 
                WHERE status = 'failed'
                ORDER BY updated_at DESC
                LIMIT ?
            """, (limit,))
            
            results = []
            columns = [desc[0] for desc in cursor.description]
            
            for row in cursor.fetchall():
                ticket_info = dict(zip(columns, row))
                
                # Parse JSON fields
                if ticket_info.get("ticket_data"):
                    ticket_info["ticket_data"] = json.loads(ticket_info["ticket_data"])
                if ticket_info.get("result_data"):
                    ticket_info["result_data"] = json.loads(ticket_info["result_data"])
                
                results.append(ticket_info)
            
            return results
    
    def get_orphaned_tickets(self) -> List[Dict[str, Any]]:
        """Get tickets that were created but queue operation failed.
        
        Returns:
            List of potentially orphaned tickets
        """
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute("""
                SELECT * FROM ticket_registry 
                WHERE ticket_id IS NOT NULL 
                AND status IN ('processing', 'failed')
                ORDER BY updated_at DESC
            """)
            
            results = []
            columns = [desc[0] for desc in cursor.description]
            
            for row in cursor.fetchall():
                ticket_info = dict(zip(columns, row))
                
                # Parse JSON fields
                if ticket_info.get("ticket_data"):
                    ticket_info["ticket_data"] = json.loads(ticket_info["ticket_data"])
                if ticket_info.get("result_data"):
                    ticket_info["result_data"] = json.loads(ticket_info["result_data"])
                
                results.append(ticket_info)
            
            return results
    
    def attempt_recovery(self, queue_id: str, recovery_type: str) -> Dict[str, Any]:
        """Attempt to recover a failed operation.
        
        Args:
            queue_id: Queue operation ID to recover
            recovery_type: Type of recovery to attempt
            
        Returns:
            Recovery result
        """
        ticket_info = self.get_ticket_info(queue_id)
        if not ticket_info:
            return {"success": False, "error": "Ticket operation not found"}
        
        recovery_data = {
            "original_status": ticket_info["status"],
            "recovery_type": recovery_type,
            "timestamp": datetime.now().isoformat()
        }
        
        try:
            if recovery_type == "mark_completed":
                # Mark as completed if ticket ID exists
                if ticket_info.get("ticket_id"):
                    self.update_ticket_status(queue_id, "recovered", 
                                            result_data={"recovery": "marked_completed"})
                    recovery_data["success"] = True
                    recovery_data["action"] = "Marked as completed based on existing ticket ID"
                else:
                    recovery_data["success"] = False
                    recovery_data["error"] = "No ticket ID available to mark as completed"
            
            elif recovery_type == "retry_operation":
                # Reset to queued status for retry
                self.update_ticket_status(queue_id, "queued", 
                                        error_message=None,
                                        retry_count=ticket_info.get("retry_count", 0))
                recovery_data["success"] = True
                recovery_data["action"] = "Reset to queued for retry"
            
            else:
                recovery_data["success"] = False
                recovery_data["error"] = f"Unknown recovery type: {recovery_type}"
            
            # Log recovery attempt
            self._log_recovery(queue_id, recovery_type, recovery_data, recovery_data["success"])
            
            return recovery_data
            
        except Exception as e:
            recovery_data["success"] = False
            recovery_data["error"] = str(e)
            self._log_recovery(queue_id, recovery_type, recovery_data, False)
            return recovery_data
    
    def _log_recovery(
        self, 
        queue_id: str, 
        recovery_type: str, 
        recovery_data: Dict[str, Any], 
        success: bool
    ) -> None:
        """Log recovery attempt."""
        with self._lock:
            with sqlite3.connect(self.db_path) as conn:
                conn.execute("""
                    INSERT INTO recovery_log (
                        queue_id, recovery_type, recovery_data, timestamp, success
                    ) VALUES (?, ?, ?, ?, ?)
                """, (
                    queue_id,
                    recovery_type,
                    json.dumps(recovery_data),
                    datetime.now().isoformat(),
                    success
                ))
                conn.commit()
    
    def get_recovery_history(self, queue_id: str) -> List[Dict[str, Any]]:
        """Get recovery history for a queue operation.
        
        Args:
            queue_id: Queue operation ID
            
        Returns:
            List of recovery attempts
        """
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute("""
                SELECT * FROM recovery_log 
                WHERE queue_id = ?
                ORDER BY timestamp DESC
            """, (queue_id,))
            
            results = []
            columns = [desc[0] for desc in cursor.description]
            
            for row in cursor.fetchall():
                recovery_info = dict(zip(columns, row))
                if recovery_info.get("recovery_data"):
                    recovery_info["recovery_data"] = json.loads(recovery_info["recovery_data"])
                results.append(recovery_info)
            
            return results
    
    def cleanup_old_entries(self, days: int = 30) -> int:
        """Clean up old completed entries.
        
        Args:
            days: Remove entries older than this many days
            
        Returns:
            Number of entries removed
        """
        cutoff_date = (datetime.now() - timedelta(days=days)).isoformat()
        
        with self._lock:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute("""
                    DELETE FROM ticket_registry
                    WHERE status IN ('completed', 'recovered')
                    AND updated_at < ?
                """, (cutoff_date,))
                
                deleted_count = cursor.rowcount
                conn.commit()
                
                return deleted_count
