# #region TranslateRunRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, execution]
# @defgroup Api Module group.
# @BRIEF Translation Run execution, retry, and cancel routes.
# @LAYER API
# @RELATION DEPENDS_ON -> [TranslateRunHistoryRoutesModule]
# @RELATION DEPENDS_ON -> [TranslateRunEditRoutesModule]
# @RELATION CALLED_BY -> [TranslateRoutes]

from datetime import UTC, datetime

import asyncio
from fastapi import Depends, HTTPException, Query, status
from sqlalchemy.orm import Session

from ....core.config_manager import ConfigManager
from ....core.cot_logger import get_trace_id, seed_trace_id, set_trace_id
from ....core.database import SessionLocal, get_db
from ....core.logger import logger
from ....dependencies import get_config_manager, get_current_user, has_permission
from ....plugins.translate.orchestrator import TranslationOrchestrator
from ....schemas.auth import User
from ._helpers import _run_to_response
from ._router import router


# #region run_translation [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Execute a translation job (trigger a run).
# @PRE  User has translate.job.execute permission.
# @POST  Returns the created translation run.
@router.post("/jobs/{job_id}/run", status_code=status.HTTP_201_CREATED)
async def run_translation(
    job_id: str,
    full_translation: bool = Query(False, description="Translate ALL rows (skip new-key-only filter)"),
    current_user: User = Depends(get_current_user),
    _ = Depends(has_permission("translate.job", "EXECUTE")),
    db: Session = Depends(get_db),
    config_manager: ConfigManager = Depends(get_config_manager),
):
    """Execute a translation job (trigger a run).

    - Normal run (default): translates only new/changed rows
    - Full translation (full_translation=true): re-translates ALL rows from the datasource
    """
    logger.reason(
        f"run_translation — Job: {job_id}, User: {current_user.username}, full: {full_translation}",
        extra={"src": "translate_routes"},
    )
    try:
        orch = TranslationOrchestrator(db, config_manager, current_user.username)
        run = orch.start_run(job_id=job_id, is_scheduled=False, full_translation=full_translation)
        # The request-scoped db session will be closed after this handler returns.
        # The background task must use its OWN session.
        _request_trace_id = get_trace_id()

        async def _background_execute():
            from ....models.translate import TranslationRun as TRModel
            # Preserve request's trace_id for log correlation
            if _request_trace_id:
                set_trace_id(_request_trace_id)
            else:
                seed_trace_id()
            bg_db = None
            try:
                bg_db = SessionLocal()
                bg_orch = TranslationOrchestrator(
                    bg_db, config_manager,
                    current_user.username if current_user else None,
                )
                bg_run = bg_db.query(TRModel).filter(TRModel.id == run.id).first()
                if bg_run is None:
                    logger.explore(
                        "Background execute: run not found",
                        extra={"src": "translate_routes", "run_id": run.id},
                    )
                    return

                await bg_orch.execute_run(bg_run)

                check_run = bg_db.query(TRModel).filter(TRModel.id == run.id).first()
                if check_run and check_run.status in ("COMPLETED", "FAILED", "CANCELLED"):
                    logger.reason(
                        "Background execute verified",
                        extra={"src": "translate_routes", "run_id": run.id, "status": check_run.status},
                    )
                else:
                    actual_status = check_run.status if check_run else "NOT_FOUND"
                    logger.explore(
                        "Background execute: run not in terminal state after commit",
                        extra={"src": "translate_routes", "run_id": run.id, "status": actual_status},
                    )
                    if check_run:
                        check_run.status = "FAILED"
                        check_run.error_message = f"Background execution did not reach terminal state (was: {actual_status})"
                        bg_db.commit()

            except Exception as bg_err:
                logger.explore(
                    "Background execute failed",
                    extra={"src": "translate_routes", "run_id": run.id, "error": str(bg_err)},
                )
                try:
                    fb_db = SessionLocal()
                    try:
                        fb_run = fb_db.query(TRModel).filter(TRModel.id == run.id).first()
                        if fb_run:
                            fb_run.status = "FAILED"
                            fb_run.error_message = f"Background execute error: {bg_err}"
                            fb_run.completed_at = datetime.now(UTC)
                            fb_db.commit()
                            logger.reason("Background execute: run marked FAILED",
                                          extra={"src": "translate_routes", "run_id": run.id})
                    finally:
                        fb_db.close()
                except Exception as fb_err:
                    logger.explore("Background execute: unable to mark run FAILED",
                                   extra={"src": "translate_routes", "run_id": run.id, "error": str(fb_err)})
            finally:
                if bg_db is not None:
                    bg_db.close()

        asyncio.create_task(_background_execute())
        return _run_to_response(run)
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
    except Exception as e:
        logger.explore("run_translation failed", extra={"src": "translate_routes", "error": str(e)})
        raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f"Run failed: {e}")
# #endregion run_translation


# #region retry_run [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Retry failed batches in a translation run.
# @PRE  User has translate.job.execute permission.
# @POST  Returns the updated translation run.
@router.post("/runs/{run_id}/retry")
async def retry_run(
    run_id: str,
    current_user: User = Depends(get_current_user),
    _ = Depends(has_permission("translate.job", "EXECUTE")),
    db: Session = Depends(get_db),
    config_manager: ConfigManager = Depends(get_config_manager),
):
    """Retry failed batches in a translation run."""
    logger.reason(f"retry_run — Run: {run_id}, User: {current_user.username}", extra={"src": "translate_routes"})
    try:
        orch = TranslationOrchestrator(db, config_manager, current_user.username)
        run = await orch.retry_failed_batches(run_id)
        return _run_to_response(run)
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
    except Exception as e:
        logger.explore("retry_run failed", extra={"src": "translate_routes", "error": str(e)})
        raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f"Retry failed: {e}")
# #endregion retry_run


# #region retry_insert [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Retry the SQL insert phase for a completed run.
# @PRE  User has translate.job.execute permission.
# @POST  Returns the updated run.
@router.post("/runs/{run_id}/retry-insert")
async def retry_insert(
    run_id: str,
    current_user: User = Depends(get_current_user),
    _ = Depends(has_permission("translate.job", "EXECUTE")),
    db: Session = Depends(get_db),
    config_manager: ConfigManager = Depends(get_config_manager),
):
    """Retry the SQL insert phase for a completed run."""
    logger.reason(f"retry_insert — Run: {run_id}, User: {current_user.username}", extra={"src": "translate_routes"})
    try:
        orch = TranslationOrchestrator(db, config_manager, current_user.username)
        run = await orch.retry_insert(run_id)
        return _run_to_response(run)
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
    except Exception as e:
        logger.explore("retry_insert failed", extra={"src": "translate_routes", "error": str(e)})
        raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f"Retry insert failed: {e}")
# #endregion retry_insert


# #region cancel_run [C:4] [TYPE Function]
# @ingroup Api
# @BRIEF Cancel a running translation.
# @PRE  User has translate.job.execute permission.
# @POST  Run is cancelled.
@router.post("/runs/{run_id}/cancel")
def cancel_run(
    run_id: str,
    current_user: User = Depends(get_current_user),
    _ = Depends(has_permission("translate.job", "EXECUTE")),
    db: Session = Depends(get_db),
    config_manager: ConfigManager = Depends(get_config_manager),
):
    """Cancel a running translation."""
    logger.reason(f"cancel_run — Run: {run_id}, User: {current_user.username}", extra={"src": "translate_routes"})
    try:
        orch = TranslationOrchestrator(db, config_manager, current_user.username)
        run = orch.cancel_run(run_id)
        return _run_to_response(run)
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
# #endregion cancel_run


# #endregion TranslateRunRoutesModule
