# endoreg_db/services/anonymization.py
import logging
from pathlib import Path
from typing import Optional, Literal

from django.db import transaction

from endoreg_db.models import RawPdfFile, VideoFile
from endoreg_db.services.video_import import VideoImportService
from endoreg_db.services.report_import import ReportImportService
from endoreg_db.utils.paths import STORAGE_DIR
from endoreg_db.utils.storage import ensure_local_file, file_exists

logger = logging.getLogger(__name__)


class AnonymizationService:
    """
    Orchestrates long‑running anonymization tasks so the view only
    does HTTP <-> Service translation.
    """

    def __init__(self, project_root: Optional[Path] = None):
        """
        Initialize the AnonymizationService with service instances.

        Args:
            project_root: Path to the project root. If None, uses settings.BASE_DIR
        """
        self.project_root: Path = project_root or STORAGE_DIR
        self.video_service = VideoImportService()
        self.pdf_service = ReportImportService()

    @staticmethod
    def get_status(file_id: int, kind: Optional[str] = None) -> Optional[dict]:
        """
        Retrieve status. 
        Handles 'pdf' vs 'report' alias.
        If kind is None, checks both tables (Video priority).
        """
        
        # 1. Normalize the input kind if legacy name pdf is used
        if kind == 'pdf':
            kind = 'report'
            
        # 2. Define lookup logic
        check_video = kind == "video" or kind is None
        check_report = kind == "report" or kind is None

        # 3. Check VideoFile
        if check_video:
            vf = (
                VideoFile.objects.select_related("state", "sensitive_meta")
                .filter(pk=file_id)
                .first()
            )
            if vf:
                return {
                    "mediaType": "video",
                    "anonymizationStatus": vf.state.anonymization_status
                    if vf.state
                    else "not_started",
                    "fileExists": file_exists(vf.raw_file),
                    "uuid": str(vf.video_hash) if vf.video_hash else None,
                }

        # 4. Check RawPdfFile
        if check_report:
            pdf = (
                RawPdfFile.objects.select_related("state", "sensitive_meta")
                .filter(pk=file_id)
                .first()
            )
            if pdf:
                return {
                    "mediaType": "pdf",
                    "anonymizationStatus": pdf.state.anonymization_status
                    if pdf.state
                    else "not_started",
                    "fileExists": file_exists(pdf.file),
                    "hash": pdf.pdf_hash,
                }
                
        # 5. Not found in either (or the specific requested type wasn't found)
        return None

    # ---------- COMMANDS ------------------------------------------------
    @transaction.atomic
    def start(self, file_id: int, kind: Optional[str] = None) -> Optional[str]:
        """
        Start anonymization process for a file by its ID.

        Args:
            file_id: The ID of the file to anonymize

        Returns:
            str or None: Media type if successful, None if file not found
        """
        # Try VideoFile first
        if kind == "video" or kind is None:
            vf = (
                VideoFile.objects.select_related(
                    "state", "sensitive_meta", "center", "video_meta__processor"
                )
                .filter(pk=file_id)
                .first()
            )
            if vf:
                try:
                    logger.info(f"Starting video anonymization for VideoFile ID: {file_id}")

                    # Check if already processed
                    if vf.state and vf.state.anonymized:
                        logger.info(f"VideoFile {file_id} already anonymized, skipping")
                        return "video"

                    # Get file path
                    file_path = vf.get_raw_file_path()
                    if not file_path or not Path(file_path).exists():
                        logger.error(
                            f"Raw file not found for VideoFile {file_id}: {file_path}"
                        )
                        return None

                    # Get processor name
                    processor_name = None
                    if vf.video_meta and vf.video_meta.processor:
                        processor_name = vf.video_meta.processor.name
                    elif hasattr(vf, "processor") and vf.processor:
                        processor_name = vf.processor.name

                    # Get center name
                    center_name = vf.center.name if vf.center else "unknown_center"

                    # Mark as started
                    if vf.state:
                        vf.state.processing_started = True
                        vf.state.save(update_fields=["processing_started"])

                    # Use VideoImportService for anonymization
                    safe_processor_name = processor_name or "unknown_processor"
                    self.video_service.import_and_anonymize(
                        file_path=file_path,
                        center_name=center_name,
                        processor_name=safe_processor_name,
                    )

                    logger.info(
                        f"Video anonymization completed for VideoFile ID: {file_id}"
                    )
                    return "video"

                except Exception as e:
                    logger.error(f"Failed to anonymize VideoFile {file_id}: {e}")
                    # Mark as failed if state exists
                    if vf.state:
                        vf.state.processing_started = (
                            False  # Mark processing as not started due to failure
                        )
                        vf.state.save(update_fields=["processing_started"])
                    raise
        elif kind == "report" or kind is None:
            # Try RawPdfFile
            pdf = (
                RawPdfFile.objects.select_related("state", "sensitive_meta", "center")
                .filter(pk=file_id)
                .first()
            )
            if pdf:
                try:
                    logger.info(f"Starting report processing for RawPdfFile ID: {file_id}")

                    # Check if already processed
                    if pdf.state and getattr(pdf.state, "anonymized", False):
                        logger.info(f"RawPdfFile {file_id} already processed, skipping")
                        return "pdf"

                    file_field = pdf.file
                    if not file_field or not file_field.name:
                        logger.error(f"report file not found for RawPdfFile {file_id}")
                        return None

                    if not file_exists(file_field):
                        logger.error(
                            "report file missing from storage for RawPdfFile %s", file_id
                        )
                        return None

                    # Get center name
                    center_name = pdf.center.name if pdf.center else "unknown_center"

                    # Mark as started
                    if pdf.state:
                        pdf.state.processing_started = True
                        pdf.state.save(update_fields=["processing_started"])

                    with ensure_local_file(file_field) as local_path:
                        self.pdf_service.import_and_anonymize(
                            file_path=local_path,
                            center_name=center_name,
                        )

                    logger.info(f"report processing completed for RawPdfFile ID: {file_id}")
                    return "pdf"

                except Exception as e:
                    logger.error(f"Failed to process RawPdfFile {file_id}: {e}")
                    # Mark as failed if state exists
                    if pdf.state and hasattr(pdf.state, "processing_failed"):
                        pdf.state.save(update_fields=["processing_failed"])
                    elif pdf.sensitive_meta and hasattr(
                        pdf.sensitive_meta, "processing_failed"
                    ):
                        pdf.sensitive_meta.save(update_fields=["processing_failed"])
                    raise

            logger.warning(f"No file found with ID: {file_id}")
            return None

    @staticmethod
    @transaction.atomic
    def validate(file_id: int) -> None | Literal['video'] | Literal['pdf']:
        from endoreg_db.views.anonymization.validate import AnonymizationValidateView
        
        vf = VideoFile.objects.select_related("state").filter(pk=file_id).first()
        if vf:
            state = vf.state or vf.get_or_create_state()
            if hasattr(state, "mark_anonymization_validated"):
                state.mark_anonymization_validated()
            return "video"

        pdf = RawPdfFile.objects.select_related("state").filter(pk=file_id).first()
        if pdf:
            state = pdf.state or pdf.get_or_create_state()
            if hasattr(state, "mark_anonymization_validated"):
                state.mark_anonymization_validated()
            return "pdf"

        return None

    @staticmethod
    def list_items():
        video_files = VideoFile.objects.select_related("state").all()
        pdf_files = RawPdfFile.objects.select_related(
            "state"
        ).all()  # was sensitive_meta

        data = []
        for vf in video_files:
            data.append(
                {
                    "id": vf.pk,
                    "mediaType": "video",
                    "anonymizationStatus": vf.state.anonymization_status
                    if vf.state
                    else "not_started",
                    "createdAt": vf.date_created,
                    "updatedAt": vf.date_modified,
                }
            )

        for pdf in pdf_files:
            data.append(
                {
                    "id": pdf.pk,
                    "mediaType": "pdf",
                    "anonymizationStatus": pdf.state.anonymization_status
                    if pdf.state
                    else "not_started",
                    "createdAt": pdf.date_created,
                    "updatedAt": pdf.date_modified,
                }
            )
        return data
