from datetime import datetime, timezone
from typing import Optional
import uuid

from sqlalchemy.orm import Session

from app.models.transkripsi import TranskripsiTask


def create_task(
    db:            Session,
    model_type:    str,
    file_path:     str,
    engine:        str,
    is_court_mode: bool = False,
    user_id:       Optional[int] = None,
) -> TranskripsiTask:
    """Buat task baru dan simpan ke database."""
    task = TranskripsiTask(
        task_uuid     = str(uuid.uuid4()),
        status        = "pending",
        progress      = 5,
        model_type    = model_type,
        audio_file    = file_path,
        engine        = engine,
        is_court_mode = is_court_mode,
        is_cancelled  = False,
        created_at    = datetime.now(timezone.utc),
        updated_at    = datetime.now(timezone.utc),
    )

    if user_id is not None and hasattr(TranskripsiTask, "user_id"):
        task.user_id = user_id

    db.add(task)
    db.commit()
    db.refresh(task)
    return task


def get_task_by_id(db: Session, task_id: int) -> Optional[TranskripsiTask]:
    return db.query(TranskripsiTask).filter(TranskripsiTask.id == task_id).first()


def get_task_by_uuid(db: Session, task_uuid: str) -> Optional[TranskripsiTask]:
    return db.query(TranskripsiTask).filter(TranskripsiTask.task_uuid == task_uuid).first()


def update_task_status(
    db:              Session,
    task:            TranskripsiTask,
    status:          str,
    progress:        int,
    result:          Optional[str]   = None,
    error_message:   Optional[str]   = None,
    processing_time: Optional[float] = None,
    audio_duration:  Optional[float] = None,
    partial_result:  Optional[str]   = None,
    segments_json:   Optional[str]   = None,
) -> None:
    """
    Update status task.

    FIX BUG: Fungsi ini hanya mengupdate field yang diberikan secara eksplisit.
    partial_result dan segments_json hanya diupdate jika nilainya tidak None
    (string kosong "" tetap di-set, berbeda dari None = tidak diubah).
    """
    task.status     = status
    task.progress   = progress
    task.updated_at = datetime.now(timezone.utc)

    if result is not None:
        task.result = result
    if error_message is not None:
        task.error_message = error_message
    if processing_time is not None:
        task.processing_time = processing_time
    if audio_duration is not None:
        task.audio_duration = audio_duration
    if partial_result is not None:
        task.partial_result = partial_result
    if segments_json is not None:
        task.segments_json = segments_json

    db.commit()
    db.refresh(task)


def request_cancel_task(db: Session, task_id: int) -> Optional[TranskripsiTask]:
    """
    Tandai task sebagai 'diminta untuk dibatalkan'.
    Worker akan cek flag ini dan berhenti saat segmen berikutnya selesai.

    Returns task jika ditemukan, None jika tidak ada.
    Raises ValueError jika task tidak dalam status yang bisa dibatalkan.
    """
    task = get_task_by_id(db, task_id)
    if not task:
        return None

    if task.status not in ("pending", "processing"):
        raise ValueError(
            f"Task #{task_id} tidak bisa dibatalkan karena statusnya '{task.status}'."
        )

    task.is_cancelled = True
    task.updated_at   = datetime.now(timezone.utc)
    db.commit()
    db.refresh(task)
    return task


def is_task_cancelled(db: Session, task_id: int) -> bool:
    """
    Cek apakah flag cancel sudah di-set untuk task ini.
    Dipakai oleh worker di thread pool — buka sesi DB baru setiap cek
    agar membaca nilai terbaru dari disk, bukan dari cache sesi lama.
    """
    task = db.query(TranskripsiTask).filter(
        TranskripsiTask.id == task_id
    ).first()
    return bool(task and task.is_cancelled)