from datetime import datetime
from uuid import uuid4
import base64
import os
import json
import logging
from fastapi import APIRouter, UploadFile, File, Form, HTTPException, Depends, Query
from sqlalchemy.orm import Session
from typing import Optional, Literal, Dict, Any

from core.celery_app import celery_app
from core.config import settings
from core.database import get_core_db
from core.paths import layout_path
from core.security import get_current_user, validate_access_to_resource

from .tasks import (
    scan_file,
    insert_valid_rows,
    IMPORT_FILE_KEY_PREFIX,
    IMPORT_META_KEY_PREFIX,
    IMPORT_REDIS_TTL,
)
from .schemas import ImportJobResponse, ImportJobStatus, CommitRequest

router = APIRouter()
logger = logging.getLogger(__name__)


def _get_redis():
    """Redis client (same broker as Celery so worker can read)."""
    import redis
    url = os.getenv("VALKEY_URL", os.getenv("REDIS_URL", "redis://valkey:6379/0"))
    return redis.Redis.from_url(url, decode_responses=False)

@router.post("/upload/{model_target}", response_model=ImportJobResponse)
async def upload_import_file(
    model_target: Literal["invoice_header", "invoice_details", "invoice_series"],
    file: UploadFile = File(...),
    footer_config: Optional[str] = Form(None),  # JSON string with settings
    template_id: Optional[str] = Form(None),  # id de la plantilla (ej. imp_temp_header) para respetar columnas
    company_id: int = Query(..., description="Company ID"),  # Required for context
    operation_type: Optional[str] = Query("imp"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Step 1: Upload CSV, save to temp, trigger scan task.
    Si se envía template_id, solo se leen las columnas de esa plantilla.
    """
    # 1. Validate Access & Get Tenant
    try:
        tenant_id = validate_access_to_resource(db, company_id, current_user)
    except Exception as e:
        logger.error(f"Access validation failed: {e}")
        raise HTTPException(status_code=403, detail="Invalid company access")

    if not file.filename.endswith(".csv"):
        raise HTTPException(status_code=400, detail="Only .csv files allowed")

    job_id = str(uuid4())
    contents = await file.read()

    meta_data = {
        "tenant_id": tenant_id,
        "company_id": company_id,
        "user_id": current_user.get("id"),
        "capture_user": (
            current_user.get("preferred_username")
            or current_user.get("email")
            or current_user.get("sub")
            or "CSV"
        ),
        "footer_config": footer_config,
        "operation_type": operation_type,
        "template_id": template_id,
    }

    # Store file and meta in Redis so the Celery worker can read them (no shared filesystem needed)
    try:
        redis_client = _get_redis()
        redis_client.set(
            f"{IMPORT_FILE_KEY_PREFIX}{job_id}",
            base64.b64encode(contents),
            ex=IMPORT_REDIS_TTL,
        )
        redis_client.set(
            f"{IMPORT_META_KEY_PREFIX}{job_id}",
            json.dumps(meta_data).encode("utf-8"),
            ex=IMPORT_REDIS_TTL,
        )
    except Exception as e:
        logger.error(f"Redis store error: {e}")
        raise HTTPException(status_code=500, detail="Failed to queue file for processing.")

    # Optional: also write to local disk (e.g. for same-machine worker or debugging)
    try:
        upload_dir = layout_path("imports", "temp")
        os.makedirs(upload_dir, exist_ok=True)
        file_path = os.path.join(upload_dir, f"{job_id}.csv")
        meta_path = os.path.join(upload_dir, f"{job_id}.meta.json")
        with open(file_path, "wb") as f:
            f.write(contents)
        with open(meta_path, "w") as f:
            json.dump(meta_data, f)
    except Exception as e:
        logger.warning(f"Local file save failed (worker will use Redis): {e}")

    # Trigger Celery Task (Async). Worker loads file from Redis.
    scan_file.apply_async(args=[job_id, model_target, footer_config], task_id=job_id)
    
    return ImportJobResponse(
        job_id=job_id,
        status="queued",
        message="File uploaded. Scanning started."
    )

@router.get("/{job_id}/status")
async def get_import_status(job_id: str):
    """
    Poll to get progress or final report. Always returns an object with "status".
    """
    task_result = celery_app.AsyncResult(job_id)

    if task_result.state == "PENDING":
        return {"status": "processing", "progress": 0}
    if task_result.state == "PROGRESS":
        return {
            "status": "processing",
            "progress": (task_result.info or {}).get("current", 0),
            "total": (task_result.info or {}).get("total", 0),
        }
    if task_result.state == "SUCCESS":
        result = task_result.result
        if isinstance(result, dict) and "status" in result:
            return result
        return {"status": "finished", "result": result}
    # FAILURE: obtener mensaje real (traceback, result o get(propagate=False))
    logger.warning("Import task %s failed: state=%s", job_id, task_result.state)
    err_msg = None
    tb = getattr(task_result, "traceback", None)
    if tb:
        logger.debug("Task traceback: %s", tb[:500] if isinstance(tb, str) else tb)
    if tb and isinstance(tb, str):
        lines = [l.strip() for l in tb.strip().split("\n") if l.strip()]
        if lines:
            err_msg = lines[-1]
        if not err_msg and len(lines) > 1:
            err_msg = lines[-2] + " " + (lines[-1] or "")
    if not err_msg:
        try:
            exc = task_result.get(propagate=False)
            if exc is not None:
                err_msg = str(exc)
        except Exception:
            pass
    if not err_msg:
        result = getattr(task_result, "result", None)
        info = getattr(task_result, "info", None)
        if result is not None and not isinstance(result, dict):
            err_msg = str(result)
        elif isinstance(result, dict) and (result.get("error") or result.get("message")):
            err_msg = result.get("error") or result.get("message")
        if not err_msg and isinstance(info, str):
            err_msg = info
        elif not err_msg and isinstance(info, dict) and "error" in info:
            err_msg = str(info["error"])
    if not err_msg:
        err_msg = "Task failed"
    return {"status": "failed", "error": err_msg}


@router.post("/{job_id}/commit")
async def commit_import_job(job_id: str, body: CommitRequest):
    """
    Step 2: User confirms import. Trigger bulk insert.
    """
    task = insert_valid_rows.delay(job_id, body.model_target)
    
    return {
        "status": "committing",
        "message": "Bulk insert started.",
        "commit_job_id": task.id
    }
