# api/jobs.py - FINAL VERSION - Use this one!
from fastapi import (
    APIRouter,
    Depends,
    HTTPException,
    UploadFile,
    File,
    Form,
    Header,
)
from fastapi.responses import RedirectResponse
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Optional, Dict, Any, Literal
from uuid import UUID, uuid4
from datetime import datetime
import hashlib
import json
from pydantic import BaseModel
import redis.asyncio as aioredis
import logging

from ..services.job_service import JobService
from ..auth import get_current_account
from ..db import get_session
from ..settings import REDIS_URL
from .progress import get_redis
from .v2.jobs import read_csv_with_encoding

log = logging.getLogger(__name__)

router = APIRouter(prefix="/api/v1/jobs", tags=["jobs"])


# ===== Pydantic Models =====
class JobResponse(BaseModel):
    job_id: str
    status: str
    project_id: str
    estimated_duration_seconds: Optional[int]
    _links: Dict[str, str]


class JobCreationRequest(BaseModel):
    mode: Literal["match", "dedupe"]
    project_id: Optional[UUID] = None
    config_overrides: Optional[Dict[str, Any]] = None
    auto_detect: bool = True


# ===== Dependency Providers =====
async def get_redis_queue():
    """Provide Redis connection for queue operations"""
    redis = await aioredis.from_url(REDIS_URL)
    try:
        yield redis
    finally:
        await redis.close()


async def get_job_service(
    db: AsyncSession = Depends(get_session),
    redis: aioredis.Redis = Depends(get_redis_queue),
) -> JobService:
    """Provide JobService with all its dependencies"""
    from ..ml.smart_detection import SmartAutoDetector
    from ..storage import get_storage_backend

    return JobService(
        db_session=db,
        redis_client=redis,
        storage=get_storage_backend(),
        ml_detector=SmartAutoDetector(),
    )


# ===== API Endpoints =====
@router.post("")  # This creates POST /api/v2/jobs
async def create_job(
    source_file: UploadFile = File(...),
    reference_file: Optional[UploadFile] = File(None),
    mode: str = Form("match"),
    idempotency_key: Optional[str] = Header(default=None),
    db: AsyncSession = Depends(get_session),
    account_id: UUID = Depends(get_current_account),
):
    """
    Create a new job with file uploads (v2 flow)
    Properly writes to BOTH SQL and Redis for consistency.
    """
    # Get async Redis connection
    redis_conn = await get_redis()
    if not redis_conn:
        raise HTTPException(503, "Cache service unavailable")

    log.info("=== CREATE JOB DEBUG ===")
    log.info(f"Source file: {source_file.filename}")
    log.info(f"Reference file: {reference_file.filename if reference_file else 'None'}")
    log.info(f"Mode: {mode}")
    log.info(f"Idempotency key: {idempotency_key}")

    try:
        # Read and hash files
        source_content = await source_file.read()
        source_hash = hashlib.md5(source_content).hexdigest()
        log.info(
            f"Source content size: {len(source_content)} bytes, hash: {source_hash}"
        )

        ref_hash = None
        ref_content = None
        if reference_file:
            ref_content = await reference_file.read()
            ref_hash = hashlib.md5(ref_content).hexdigest()
            log.info(
                f"Reference content size: {len(ref_content)} bytes, hash: {ref_hash}"
            )

        # Compute fingerprint for idempotency
        content_fp = f"{account_id}:{mode}:{source_hash}:{ref_hash or ''}"
        idem_key = (
            f"idem:{account_id}:{idempotency_key}"
            if idempotency_key
            else f"idem:{content_fp}"
        )

        # Check for existing job (idempotency)
        existing_job_id = await redis_conn.get(idem_key)
        if existing_job_id:
            job_id = (
                existing_job_id.decode()
                if isinstance(existing_job_id, bytes)
                else existing_job_id
            )
            stored = await redis_conn.get(f"job:{job_id}")
            if stored:
                log.info(f"Idempotent hit -> returning existing job_id={job_id}")
                return json.loads(stored)

        # Parse files with encoding detection
        log.info(f"Processing source file: {source_file.filename}")
        source_df = read_csv_with_encoding(source_content)
        log.info(
            f"Source DataFrame: {len(source_df)} rows, {len(source_df.columns)} columns"
        )

        ref_df = None
        if ref_content:
            log.info(f"Processing reference file: {reference_file.filename}")
            ref_df = read_csv_with_encoding(ref_content)
            log.info(
                f"Reference DataFrame: {len(ref_df)} rows, {len(ref_df.columns)} columns"
            )

        # Generate new job ID - KEEP HYPHENATED
        job_id = str(uuid4())
        log.info(f"Generated job_id: {job_id}")

        # Prepare metadata
        metadata = {
            "job_id": job_id,
            "mode": mode,
            "source_file": source_file.filename,
            "ref_file": reference_file.filename if reference_file else None,
            "source_rows": len(source_df),
            "ref_rows": len(ref_df) if ref_df is not None else 0,
            "source_cols": list(source_df.columns),
            "ref_cols": list(ref_df.columns) if ref_df is not None else [],
            "source_hash": source_hash,
            "reference_hash": ref_hash if ref_hash else None,
            "status": "created",
            "created_at": datetime.utcnow().isoformat(),
        }

        # STEP 1: Write to SQL Database (source of truth for job existence)
        from ...models import Job

        job_record = Job(
            id=job_id,  # Use hyphenated UUID as-is
            account_id=account_id,
            status="created",
            config=metadata,
        )
        db.add(job_record)
        await db.commit()
        log.info(f"Created SQL job record for {job_id}")

        # STEP 2: Write to Redis (for fast access and data storage)
        # Store file data in Redis for configuration step
        await redis_conn.set(f"job:{job_id}:source_data", source_content, ex=3600)
        await redis_conn.set(f"job:{job_id}:source_hash", source_hash, ex=3600)
        log.info(f"Stored source_data and source_hash for job:{job_id}")

        if ref_content:
            await redis_conn.set(f"job:{job_id}:ref_data", ref_content, ex=3600)
            await redis_conn.set(f"job:{job_id}:ref_hash", ref_hash, ex=3600)
            log.info(f"Stored ref_data and ref_hash for job:{job_id}")

        # Store metadata in Redis
        await redis_conn.set(f"job:{job_id}", json.dumps(metadata), ex=3600)

        # Remember for idempotency (24h)
        await redis_conn.set(idem_key, job_id, ex=86400)

        response = {
            "job_id": job_id,
            "status": "created",
            "mode": mode,
            "source_file": source_file.filename,
            "reference_file": reference_file.filename if reference_file else None,
            "_links": {
                "self": f"/api/v2/jobs/{job_id}",
                "configure": f"/api/v2/jobs/{job_id}/configuration/suggest",
                "execute": f"/api/v2/jobs/{job_id}/execute",
            },
        }

        log.info(f"Returning response: {response}")
        return response

    except Exception as e:
        log.error(f"Error creating job: {e}", exc_info=True)
        await db.rollback()  # Rollback SQL on error
        raise HTTPException(500, f"Failed to create job: {str(e)}")


# COMMENTED OUT - This creates a duplicate route as it redirects to itself
# The main status route is already defined in main.py line 870
# @router.get("/{job_id:path}/status", include_in_schema=False)
# async def legacy_status(job_id: str):
#     """Redirect to canonical public/iPaaS status route to avoid duplicate handlers."""
#     return RedirectResponse(url=f"/api/v1/jobs/{job_id}/status", status_code=307)


@router.get("/{job_id}/results")
async def get_job_results(
    job_id: UUID,
    format: Literal["json", "csv", "parquet"] = "json",
    limit: Optional[int] = None,
    offset: int = 0,
    db: AsyncSession = Depends(get_session),
    account_id: UUID = Depends(get_current_account),
):
    """Get job results in various formats - redirects to v2 endpoint"""

    # Redirect to v2 endpoint
    params = f"?format={format}"
    if limit is not None:
        params += f"&limit={limit}"
    params += f"&offset={offset}"

    return RedirectResponse(
        url=f"/api/v2/jobs/{job_id}/results{params}",
        status_code=307,  # Temporary redirect preserving method
    )


@router.get("/{job_id}/export")
async def export_job_results(
    job_id: UUID,
    format: Literal["json", "csv", "parquet"] = "csv",
    db: AsyncSession = Depends(get_session),
    account_id: UUID = Depends(get_current_account),
):
    """Export job results - v1 shim for backward compatibility"""

    # Redirect to v2 results endpoint with format parameter
    return RedirectResponse(
        url=f"/api/v2/jobs/{job_id}/results?format={format}",
        status_code=307,  # Temporary redirect preserving method
    )
