"""Celery tasks for async document processing."""

from uuid import UUID

import redis as sync_redis
import structlog

from src.core.config import settings
from src.core.exceptions import ExtractionError
from src.db.session import get_sync_session
from src.extraction.pipeline import ExtractionPipeline
from src.extraction.symbol_resolver import SymbolResolver
from src.workers.celery_app import celery_app

logger = structlog.get_logger(__name__)


# Context value for custom symbols (must match symbol_service.py)
CUSTOM_SYMBOL_CONTEXT = "api_upload"


@celery_app.task(name="src.workers.tasks.process_document", bind=True, max_retries=3)
def process_document(self, document_id: str) -> dict:
    """Process uploaded document through the extraction pipeline.

    This task orchestrates the complete extraction workflow:
    1. Load and validate Excel workbook
    2. Detect table boundaries (TableDetector)
    3. Extract multi-level headers (HeaderExtractor)
    4. Resolve merged cells (MergedCellResolver)
    5. Build structured records (RecordBuilder)
    6. Store records in database
    7. Update document status

    Args:
        self: Celery task instance (injected by bind=True).
        document_id: UUID string of the document to process.

    Returns:
        Dictionary with extraction summary including:
        - document_id
        - sheets_processed
        - tables_detected
        - records_extracted
        - duration_ms
        - warnings

    Raises:
        ExtractionError: If extraction fails after max retries.
    """
    doc_uuid = UUID(document_id)

    logger.info(
        "document_processing_started",
        document_id=document_id,
        task_id=self.request.id,
    )

    try:
        # Create Redis client for progress tracking (optional)
        redis_client = None
        try:
            redis_client = sync_redis.from_url(
                settings.redis_url, decode_responses=False
            )
            logger.debug(
                "redis_connected_for_progress",
                document_id=document_id,
            )
        except Exception as e:
            logger.warning(
                "redis_connection_failed",
                document_id=document_id,
                error=str(e),
                note="Proceeding without progress tracking",
            )

        # Process document through extraction pipeline
        with get_sync_session() as session:
            pipeline = ExtractionPipeline(
                db_session=session, redis_client=redis_client
            )
            summary = pipeline.process_document(doc_uuid)

        logger.info(
            "document_processing_completed",
            document_id=document_id,
            task_id=self.request.id,
            sheets_processed=summary.sheets_processed,
            tables_detected=summary.tables_detected,
            records_extracted=summary.records_extracted,
            duration_ms=summary.duration_ms,
            warnings_count=len(summary.warnings),
        )

        return summary.to_dict()

    except ExtractionError as e:
        logger.error(
            "document_extraction_failed",
            document_id=document_id,
            task_id=self.request.id,
            error_code=e.code if hasattr(e, "code") else "EXTRACTION_ERROR",
            error_message=str(e),
            retry_count=self.request.retries,
        )

        # Retry on transient failures
        if self.request.retries < self.max_retries:
            logger.info(
                "document_processing_retrying",
                document_id=document_id,
                task_id=self.request.id,
                retry_count=self.request.retries + 1,
            )
            raise self.retry(exc=e, countdown=30 * (self.request.retries + 1))

        raise

    except Exception as e:
        logger.error(
            "document_processing_failed",
            document_id=document_id,
            task_id=self.request.id,
            error=str(e),
            error_type=type(e).__name__,
            retry_count=self.request.retries,
        )

        # Retry on transient failures
        if self.request.retries < self.max_retries:
            logger.info(
                "document_processing_retrying",
                document_id=document_id,
                task_id=self.request.id,
                retry_count=self.request.retries + 1,
            )
            raise self.retry(exc=e, countdown=30 * (self.request.retries + 1))

        raise


@celery_app.task(name="src.workers.tasks.re_resolve_symbols", bind=True, max_retries=3)
def re_resolve_symbols(self, document_id: str) -> dict:
    """Re-run symbol resolution for a document after custom symbols are updated.

    This task is triggered when:
    - Custom symbols are uploaded for an already-processed document
    - Custom symbols are deleted from an already-processed document

    The task:
    1. Updates document status to "reprocessing"
    2. Re-runs symbol resolution using SymbolResolver
    3. Updates document status back to "completed"

    Args:
        self: Celery task instance (injected by bind=True).
        document_id: UUID string of the document to re-resolve.

    Returns:
        Dictionary with re-resolution summary.

    Raises:
        Exception: If re-resolution fails after max retries.
    """
    from src.db.models import Document

    doc_uuid = UUID(document_id)

    logger.info(
        "symbol_re_resolution_started",
        document_id=document_id,
        task_id=self.request.id,
    )

    try:
        with get_sync_session() as session:
            # Update document status to reprocessing
            document = session.query(Document).filter(Document.id == doc_uuid).first()

            if not document:
                logger.error(
                    "document_not_found_for_re_resolution",
                    document_id=document_id,
                )
                return {"error": "Document not found", "document_id": document_id}

            previous_status = document.status
            document.status = "reprocessing"
            session.commit()

            logger.debug(
                "document_status_updated",
                document_id=document_id,
                previous_status=previous_status,
                new_status="reprocessing",
            )

            # Re-run symbol resolution
            resolver = SymbolResolver()
            summary = resolver.resolve_document(doc_uuid, session)

            # Update document status back to completed
            document.status = "completed"
            session.commit()

            logger.info(
                "symbol_re_resolution_completed",
                document_id=document_id,
                task_id=self.request.id,
                records_processed=summary.records_processed,
                symbols_resolved=summary.symbols_resolved,
                unresolved_count=summary.unresolved_count,
            )

            return {
                "document_id": document_id,
                "records_processed": summary.records_processed,
                "symbols_resolved": summary.symbols_resolved,
                "unresolved_count": summary.unresolved_count,
                "unresolved_symbols": summary.unresolved_symbols,
            }

    except Exception as e:
        logger.error(
            "symbol_re_resolution_failed",
            document_id=document_id,
            task_id=self.request.id,
            error=str(e),
            error_type=type(e).__name__,
            retry_count=self.request.retries,
        )

        # Try to reset document status on failure
        try:
            with get_sync_session() as session:
                document = (
                    session.query(Document).filter(Document.id == doc_uuid).first()
                )
                if document and document.status == "reprocessing":
                    document.status = "completed"
                    session.commit()
        except Exception:
            pass  # Best effort status reset

        # Retry on transient failures
        if self.request.retries < self.max_retries:
            logger.info(
                "symbol_re_resolution_retrying",
                document_id=document_id,
                task_id=self.request.id,
                retry_count=self.request.retries + 1,
            )
            raise self.retry(exc=e, countdown=30 * (self.request.retries + 1))

        raise
