"""V2 Processing job repository for individual transcript processing.

Provides data access layer for v2 processing job operations including CRUD operations,
status management, and progress tracking for individual transcript processing jobs.
"""

from datetime import datetime
from typing import List, Optional, Dict, Any, Protocol
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, desc

from src.database.models import V2ProcessingJob


class V2ProcessingJobRepositoryProtocol(Protocol):
    """Protocol for v2 processing job repository operations."""
    
    def create(self, transcript_id: str, job_type: str, 
               parameters: Optional[Dict] = None) -> V2ProcessingJob:
        """Create a new v2 processing job."""
        ...
    
    def get_by_id(self, job_id: int) -> Optional[V2ProcessingJob]:
        """Get v2 processing job by ID."""
        ...
    
    def get_by_transcript(self, transcript_id: str) -> List[V2ProcessingJob]:
        """Get all v2 processing jobs for a transcript."""
        ...
    
    def get_by_status(self, status: str) -> List[V2ProcessingJob]:
        """Get v2 processing jobs by status."""
        ...
    
    def update_status(self, job_id: int, status: str, 
                     progress: Optional[float] = None,
                     error_message: Optional[str] = None,
                     result_data: Optional[Dict] = None) -> Optional[V2ProcessingJob]:
        """Update v2 processing job status."""
        ...
    
    def delete(self, job_id: int) -> bool:
        """Delete a v2 processing job."""
        ...
    
    def get_pending_jobs(self, job_type: Optional[str] = None) -> List[V2ProcessingJob]:
        """Get pending v2 processing jobs."""
        ...
    
    def get_failed_jobs(self, limit: Optional[int] = None) -> List[V2ProcessingJob]:
        """Get failed v2 processing jobs."""
        ...


class V2ProcessingJobRepository:
    """Repository for v2 processing job operations.
    
    Provides clean data access layer for v2 processing job management
    including CRUD operations, status tracking, and progress management.
    """
    
    def __init__(self, session: Session):
        """Initialize repository with database session."""
        self.session = session
    
    def create(self, transcript_id: str, job_type: str, 
               parameters: Optional[Dict] = None) -> V2ProcessingJob:
        """Create a new v2 processing job.
        
        Args:
            transcript_id: Associated transcript ID
            job_type: Type of processing job (enhancement, diarization, etc.)
            parameters: Job parameters (optional)
            
        Returns:
            Created v2 processing job
        """
        job = V2ProcessingJob(
            transcript_id=transcript_id,
            job_type=job_type,
            parameters=parameters or {},
            status="pending",
            progress=0.0
        )
        
        self.session.add(job)
        self.session.commit()
        self.session.refresh(job)
        
        return job
    
    def get_by_id(self, job_id: int) -> Optional[V2ProcessingJob]:
        """Get v2 processing job by ID.
        
        Args:
            job_id: V2 processing job ID
            
        Returns:
            V2 processing job or None if not found
        """
        return self.session.query(V2ProcessingJob).filter(
            V2ProcessingJob.id == job_id
        ).first()
    
    def get_by_transcript(self, transcript_id: str) -> List[V2ProcessingJob]:
        """Get all v2 processing jobs for a transcript.
        
        Args:
            transcript_id: Transcript ID
            
        Returns:
            List of v2 processing jobs
        """
        return self.session.query(V2ProcessingJob).filter(
            V2ProcessingJob.transcript_id == transcript_id
        ).order_by(V2ProcessingJob.created_at.desc()).all()
    
    def get_by_status(self, status: str) -> List[V2ProcessingJob]:
        """Get v2 processing jobs by status.
        
        Args:
            status: Job status (pending, processing, completed, failed)
            
        Returns:
            List of v2 processing jobs with the specified status
        """
        return self.session.query(V2ProcessingJob).filter(
            V2ProcessingJob.status == status
        ).order_by(V2ProcessingJob.created_at.desc()).all()
    
    def update_status(self, job_id: int, status: str, 
                     progress: Optional[float] = None,
                     error_message: Optional[str] = None,
                     result_data: Optional[Dict] = None) -> Optional[V2ProcessingJob]:
        """Update v2 processing job status.
        
        Args:
            job_id: V2 processing job ID
            status: New status
            progress: Progress percentage (0.0 to 1.0)
            error_message: Error message if failed
            result_data: Result data if completed
            
        Returns:
            Updated v2 processing job or None if not found
        """
        job = self.get_by_id(job_id)
        if not job:
            return None
        
        # Update status
        job.status = status
        job.updated_at = datetime.utcnow()
        
        # Update progress if provided
        if progress is not None:
            job.progress = max(0.0, min(1.0, progress))  # Clamp between 0.0 and 1.0
        
        # Update error message if provided
        if error_message is not None:
            job.error_message = error_message
        
        # Update result data if provided
        if result_data is not None:
            job.result_data = result_data
        
        # Set completed_at if job is completed
        if status in ["completed", "failed"]:
            job.completed_at = datetime.utcnow()
        
        self.session.commit()
        self.session.refresh(job)
        
        return job
    
    def delete(self, job_id: int) -> bool:
        """Delete a v2 processing job.
        
        Args:
            job_id: V2 processing job ID
            
        Returns:
            True if deleted, False if not found
        """
        job = self.get_by_id(job_id)
        if not job:
            return False
        
        self.session.delete(job)
        self.session.commit()
        
        return True
    
    def get_pending_jobs(self, job_type: Optional[str] = None) -> List[V2ProcessingJob]:
        """Get pending v2 processing jobs.
        
        Args:
            job_type: Filter by job type (optional)
            
        Returns:
            List of pending v2 processing jobs
        """
        query = self.session.query(V2ProcessingJob).filter(
            V2ProcessingJob.status == "pending"
        )
        
        if job_type:
            query = query.filter(V2ProcessingJob.job_type == job_type)
        
        return query.order_by(V2ProcessingJob.created_at.asc()).all()
    
    def get_failed_jobs(self, limit: Optional[int] = None) -> List[V2ProcessingJob]:
        """Get failed v2 processing jobs.
        
        Args:
            limit: Maximum number of jobs to return
            
        Returns:
            List of failed v2 processing jobs
        """
        query = self.session.query(V2ProcessingJob).filter(
            V2ProcessingJob.status == "failed"
        ).order_by(V2ProcessingJob.updated_at.desc())
        
        if limit:
            query = query.limit(limit)
        
        return query.all()
    
    def get_completed_jobs(self, limit: Optional[int] = None) -> List[V2ProcessingJob]:
        """Get completed v2 processing jobs.
        
        Args:
            limit: Maximum number of jobs to return
            
        Returns:
            List of completed v2 processing jobs
        """
        query = self.session.query(V2ProcessingJob).filter(
            V2ProcessingJob.status == "completed"
        ).order_by(V2ProcessingJob.completed_at.desc())
        
        if limit:
            query = query.limit(limit)
        
        return query.all()
    
    def get_jobs_by_type(self, job_type: str, status: Optional[str] = None) -> List[V2ProcessingJob]:
        """Get v2 processing jobs by type and optionally by status.
        
        Args:
            job_type: Type of processing job
            status: Filter by status (optional)
            
        Returns:
            List of v2 processing jobs
        """
        query = self.session.query(V2ProcessingJob).filter(
            V2ProcessingJob.job_type == job_type
        )
        
        if status:
            query = query.filter(V2ProcessingJob.status == status)
        
        return query.order_by(V2ProcessingJob.created_at.desc()).all()
    
    def count_by_status(self, status: str) -> int:
        """Count v2 processing jobs by status.
        
        Args:
            status: Job status
            
        Returns:
            Number of jobs with the specified status
        """
        return self.session.query(V2ProcessingJob).filter(
            V2ProcessingJob.status == status
        ).count()
    
    def get_job_statistics(self) -> Dict[str, Any]:
        """Get statistics about v2 processing jobs.
        
        Returns:
            Dictionary with job statistics
        """
        total_jobs = self.session.query(V2ProcessingJob).count()
        pending_jobs = self.count_by_status("pending")
        processing_jobs = self.count_by_status("processing")
        completed_jobs = self.count_by_status("completed")
        failed_jobs = self.count_by_status("failed")
        
        return {
            "total": total_jobs,
            "pending": pending_jobs,
            "processing": processing_jobs,
            "completed": completed_jobs,
            "failed": failed_jobs,
            "success_rate": (completed_jobs / total_jobs * 100) if total_jobs > 0 else 0
        }
    
    def cleanup_old_jobs(self, days_old: int = 30) -> int:
        """Clean up old completed/failed jobs.
        
        Args:
            days_old: Delete jobs older than this many days
            
        Returns:
            Number of jobs deleted
        """
        cutoff_date = datetime.utcnow() - datetime.timedelta(days=days_old)
        
        old_jobs = self.session.query(V2ProcessingJob).filter(
            and_(
                V2ProcessingJob.status.in_(["completed", "failed"]),
                V2ProcessingJob.updated_at < cutoff_date
            )
        ).all()
        
        count = len(old_jobs)
        for job in old_jobs:
            self.session.delete(job)
        
        self.session.commit()
        
        return count
    
    def retry_failed_job(self, job_id: int) -> Optional[V2ProcessingJob]:
        """Retry a failed v2 processing job.
        
        Args:
            job_id: V2 processing job ID
            
        Returns:
            Updated v2 processing job or None if not found
        """
        job = self.get_by_id(job_id)
        if not job or job.status != "failed":
            return None
        
        # Reset job to pending status
        job.status = "pending"
        job.progress = 0.0
        job.error_message = None
        job.completed_at = None
        job.updated_at = datetime.utcnow()
        
        self.session.commit()
        self.session.refresh(job)
        
        return job
