from datetime import date
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError

from app.core.database import get_db
from app.core.dependencies import get_current_company_id
from app.models.branch import Branch
from app.schemas.job_cards import (
    JobCardCreate,
    JobCardUpdate,
    JobCardFromQuotationRequest,
    MaterialsUpdateRequest,
    LaborUpdateRequest,
    JobCardStatusChange,
    JobCardNoteCreate,
    JobCardInvoiceRequest,
)
from app.services.job_card_service import JobCardService
from app.utils.logger import get_logger, log_exception, log_error_with_context

logger = get_logger(__name__)

router = APIRouter()


def _service(db: Session) -> JobCardService:
    return JobCardService(db)


@router.get("/queue-summary")
def job_card_queue_summary(
    branch_id: Optional[str] = Query(None),
    db: Session = Depends(get_db),
    company_id: Optional[str] = Depends(get_current_company_id),
):
    """Pending and ready-to-invoice job counts for the sales menu."""
    svc = _service(db)
    if company_id and not branch_id:
        branch_ids = [b.id for b in db.query(Branch).filter(Branch.company_id == company_id).all()]
        counts = svc.queue_summary(branch_ids=branch_ids or None)
    else:
        counts = svc.queue_summary(branch_id=branch_id)
    return {"success": True, "counts": counts}


@router.get("")
def list_job_cards(
    status: Optional[str] = Query(None),
    queue: Optional[str] = Query(
        None,
        description="Sales queues: pending (draft/scheduled/in_progress) or ready_to_invoice (completed, not invoiced)",
    ),
    job_type: Optional[str] = Query(None),
    branch_id: Optional[str] = Query(None),
    customer_id: Optional[str] = Query(None),
    search: Optional[str] = Query(None, description="Search job number or description"),
    from_date: Optional[date] = Query(None),
    to_date: Optional[date] = Query(None),
    db: Session = Depends(get_db),
    company_id: Optional[str] = Depends(get_current_company_id),
):
    try:
        # When company_id is known and no explicit branch requested, scope to company's first branch
        # The service only supports single branch_id; we use it as a scope key
        effective_branch_id = branch_id
        if company_id and not branch_id:
            all_data = _service(db).list_job_cards(
                status, job_type, None, customer_id, search, from_date, to_date, queue=queue
            )
            branch_id_set = {r.id for r in db.query(Branch).filter(Branch.company_id == company_id).all()}
            data = [jc for jc in all_data if jc.get('branch_id') in branch_id_set]
            return {"success": True, "job_cards": data}
        data = _service(db).list_job_cards(
            status, job_type, effective_branch_id, customer_id, search, from_date, to_date, queue=queue
        )
        return {"success": True, "job_cards": data}
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc
    except Exception as exc:  # pragma: no cover - defensive guard for debugging
        raise HTTPException(status_code=500, detail=str(exc)) from exc


@router.post("")
def create_job_card(payload: JobCardCreate, db: Session = Depends(get_db)):
    try:
        data = _service(db).create_job_card(payload.model_dump(), user_id=None)
        return {"success": True, "job_card": data}
    except IntegrityError as e:
        db.rollback()
        error_msg = str(e.orig)
        if 'duplicate key' in error_msg.lower() or 'unique constraint' in error_msg.lower():
            if 'job_number' in error_msg:
                raise HTTPException(status_code=400, detail="A job card with this job number already exists")
            else:
                raise HTTPException(status_code=400, detail="A job card with this value already exists. Please ensure unique fields are different.")
        raise HTTPException(status_code=400, detail=f"Database constraint violation: {error_msg}")
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc
    except Exception as exc:
        db.rollback()
        raise HTTPException(status_code=500, detail=f"Error creating job card: {str(exc)}") from exc


@router.get("/technicians")
def list_job_technicians(
    role: Optional[str] = Query(None, description="Filter technicians by role"),
    branch_id: Optional[str] = Query(None, description="Filter by branch assignment"),
    search: Optional[str] = Query(None, description="Search name, username, or email"),
    db: Session = Depends(get_db),
):
    data = _service(db).list_technicians(role=role, branch_id=branch_id, search=search)
    return {"success": True, "technicians": data}


@router.post("/from-quotation/{quotation_id}")
def create_job_from_quotation(
    quotation_id: str,
    payload: JobCardFromQuotationRequest = None,
    db: Session = Depends(get_db),
):
    try:
        body = payload.model_dump(exclude_unset=True) if payload else {}
        data = _service(db).create_from_quotation(
            quotation_id,
            user_id=None,
            branch_id=body.get("branch_id"),
            job_type=body.get("job_type", "service"),
            start_date=body.get("start_date"),
            due_date=body.get("due_date"),
            technician_id=body.get("technician_id"),
            description=body.get("description"),
            notes=body.get("notes"),
        )
        return {"success": True, "job_card": data, "message": "Job card created from quotation"}
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.get("/{job_id}")
def get_job_card(job_id: str, db: Session = Depends(get_db)):
    try:
        data = _service(db).get_job_card(job_id)
        return {"success": True, "job_card": data}
    except ValueError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc


@router.put("/{job_id}")
def update_job_card(job_id: str, payload: JobCardUpdate, db: Session = Depends(get_db)):
    try:
        data = _service(db).update_job_card(job_id, payload.model_dump(exclude_unset=True), user_id=None)
        return {"success": True, "job_card": data}
    except IntegrityError as e:
        db.rollback()
        error_msg = str(e.orig)
        if 'duplicate key' in error_msg.lower() or 'unique constraint' in error_msg.lower():
            if 'job_number' in error_msg:
                raise HTTPException(status_code=400, detail="A job card with this job number already exists")
            else:
                raise HTTPException(status_code=400, detail="A job card with this value already exists. Please ensure unique fields are different.")
        raise HTTPException(status_code=400, detail=f"Database constraint violation: {error_msg}")
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc
    except Exception as exc:
        db.rollback()
        raise HTTPException(status_code=500, detail=f"Error updating job card: {str(exc)}") from exc


@router.post("/{job_id}/materials")
def update_job_materials(job_id: str, payload: MaterialsUpdateRequest, db: Session = Depends(get_db)):
    try:
        data = _service(db).update_materials(job_id, [item.model_dump() for item in payload.materials], payload.mode)
        return {"success": True, "job_card": data}
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post("/{job_id}/labor")
def update_job_labor(job_id: str, payload: LaborUpdateRequest, db: Session = Depends(get_db)):
    try:
        data = _service(db).update_labor(job_id, [item.model_dump() for item in payload.labor], payload.mode)
        return {"success": True, "job_card": data}
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post("/{job_id}/notes")
def add_job_note(job_id: str, payload: JobCardNoteCreate, db: Session = Depends(get_db)):
    if not payload.note.strip():
        raise HTTPException(status_code=400, detail="Note cannot be empty")
    data = _service(db).add_note(job_id, payload.note, author_id=None)
    return {"success": True, "note": data}


@router.post("/{job_id}/status")
def change_job_status(job_id: str, payload: JobCardStatusChange, db: Session = Depends(get_db)):
    try:
        data = _service(db).change_status(job_id, payload.status, user_id=None, auto_invoice=True)
        return {"success": True, "job_card": data}
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post("/{job_id}/invoice")
def generate_job_invoice(job_id: str, payload: JobCardInvoiceRequest, db: Session = Depends(get_db)):
    try:
        data = _service(db).generate_invoice(
            job_id,
            user_id=None,
            save_draft=payload.save_draft,
            is_cash_sale=payload.is_cash_sale,
        )
        return {"success": True, "invoice": data}
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.delete("/{job_id}")
def delete_job_card(
    job_id: str,
    force: bool = Query(False, description="Force delete even if invoiced"),
    db: Session = Depends(get_db)
):
    """
    Delete a job card and all related materials, labor, and notes.

    - **job_id**: The ID of the job card to delete
    - **force**: If true, delete even if job has been invoiced (default: false)
    """
    try:
        success = _service(db).delete_job_card(job_id, force=force)
        return {"success": success, "message": "Job card deleted successfully"}
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc
    except Exception as exc:
        # Log the full error for debugging
        import traceback
        traceback.print_exc()
        raise HTTPException(
            status_code=500,
            detail=f"Failed to delete job card: {str(exc)}"
        ) from exc
