"""
FastAPI Routes for Aperion Flow.

REST API for pipeline execution, monitoring, and management.
"""

from __future__ import annotations

import logging
from datetime import datetime, timezone
from typing import Any

from fastapi import APIRouter, FastAPI, HTTPException, Query, status
from pydantic import BaseModel, Field

from aperion_flow.definitions.pipeline import Pipeline, PipelineStatus
from aperion_flow.engine.executor import ExecutionResult, PipelineExecutor

logger = logging.getLogger(__name__)


# Request/Response Models
class StartPipelineRequest(BaseModel):
    """Request to start a pipeline execution."""

    pipeline: Pipeline
    initial_data: dict[str, Any] = Field(default_factory=dict)
    correlation_id: str | None = None


class ResumePipelineRequest(BaseModel):
    """Request to resume a pipeline from checkpoint."""

    pipeline: Pipeline
    execution_id: str
    correlation_id: str | None = None


class ExecutionStatusResponse(BaseModel):
    """Pipeline execution status response."""

    execution_id: str
    pipeline_id: str
    pipeline_name: str
    status: PipelineStatus
    stages_total: int
    stages_completed: int
    stages_failed: int
    stages_skipped: int
    started_at: datetime | None
    completed_at: datetime | None
    error: str | None = None


class HealthResponse(BaseModel):
    """Health check response."""

    status: str = "healthy"
    service: str = "aperion-flow"
    timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))


# Router
router = APIRouter(prefix="/flow", tags=["flow"])

# In-memory storage for demo (replace with proper persistence)
_executions: dict[str, ExecutionResult] = {}
_executor: PipelineExecutor | None = None


def get_executor() -> PipelineExecutor:
    """Get or create the pipeline executor."""
    global _executor
    if _executor is None:
        _executor = PipelineExecutor()
    return _executor


@router.get("/health", response_model=HealthResponse)
async def health_check() -> HealthResponse:
    """Health check endpoint."""
    return HealthResponse()


@router.post("/start", response_model=ExecutionStatusResponse, status_code=status.HTTP_202_ACCEPTED)
async def start_pipeline(request: StartPipelineRequest) -> ExecutionStatusResponse:
    """
    Start a new pipeline execution.

    The pipeline runs asynchronously. Use GET /flow/{execution_id} to check status.
    """
    executor = get_executor()

    try:
        result = await executor.execute(
            pipeline=request.pipeline,
            initial_data=request.initial_data,
            correlation_id=request.correlation_id,
        )

        # Store result
        _executions[result.execution_id] = result

        return ExecutionStatusResponse(
            execution_id=result.execution_id,
            pipeline_id=result.pipeline_id,
            pipeline_name=result.pipeline_name,
            status=result.status,
            stages_total=result.stage_count,
            stages_completed=result.stages_completed,
            stages_failed=result.stages_failed,
            stages_skipped=result.stages_skipped,
            started_at=result.started_at,
            completed_at=result.completed_at,
            error=result.error,
        )

    except Exception as e:
        logger.exception("Failed to start pipeline")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Pipeline execution failed: {e}",
        ) from e


@router.post("/resume", response_model=ExecutionStatusResponse, status_code=status.HTTP_202_ACCEPTED)
async def resume_pipeline(request: ResumePipelineRequest) -> ExecutionStatusResponse:
    """
    Resume a pipeline from its last checkpoint.

    Use this for crash recovery or to continue paused pipelines.
    """
    executor = get_executor()

    try:
        result = await executor.execute(
            pipeline=request.pipeline,
            resume_from=request.execution_id,
            correlation_id=request.correlation_id,
        )

        # Update stored result
        _executions[result.execution_id] = result

        return ExecutionStatusResponse(
            execution_id=result.execution_id,
            pipeline_id=result.pipeline_id,
            pipeline_name=result.pipeline_name,
            status=result.status,
            stages_total=result.stage_count,
            stages_completed=result.stages_completed,
            stages_failed=result.stages_failed,
            stages_skipped=result.stages_skipped,
            started_at=result.started_at,
            completed_at=result.completed_at,
            error=result.error,
        )

    except ValueError as e:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=str(e),
        ) from e
    except Exception as e:
        logger.exception("Failed to resume pipeline")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Pipeline resume failed: {e}",
        ) from e


@router.get("/{execution_id}", response_model=ExecutionStatusResponse)
async def get_execution(execution_id: str) -> ExecutionStatusResponse:
    """Get the status of a pipeline execution."""
    result = _executions.get(execution_id)
    if not result:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Execution not found: {execution_id}",
        )

    return ExecutionStatusResponse(
        execution_id=result.execution_id,
        pipeline_id=result.pipeline_id,
        pipeline_name=result.pipeline_name,
        status=result.status,
        stages_total=result.stage_count,
        stages_completed=result.stages_completed,
        stages_failed=result.stages_failed,
        stages_skipped=result.stages_skipped,
        started_at=result.started_at,
        completed_at=result.completed_at,
        error=result.error,
    )


@router.get("/{execution_id}/data")
async def get_execution_data(execution_id: str) -> dict[str, Any]:
    """Get the output data from a completed pipeline execution."""
    result = _executions.get(execution_id)
    if not result:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Execution not found: {execution_id}",
        )

    return {
        "execution_id": execution_id,
        "status": result.status.value,
        "data": result.data,
    }


@router.get("/", response_model=list[ExecutionStatusResponse])
async def list_executions(
    status_filter: PipelineStatus | None = Query(None, alias="status"),
    limit: int = Query(default=50, ge=1, le=100),
) -> list[ExecutionStatusResponse]:
    """List pipeline executions."""
    executions = list(_executions.values())

    if status_filter:
        executions = [e for e in executions if e.status == status_filter]

    # Sort by started_at descending
    executions.sort(key=lambda e: e.started_at or datetime.min, reverse=True)
    executions = executions[:limit]

    return [
        ExecutionStatusResponse(
            execution_id=e.execution_id,
            pipeline_id=e.pipeline_id,
            pipeline_name=e.pipeline_name,
            status=e.status,
            stages_total=e.stage_count,
            stages_completed=e.stages_completed,
            stages_failed=e.stages_failed,
            stages_skipped=e.stages_skipped,
            started_at=e.started_at,
            completed_at=e.completed_at,
            error=e.error,
        )
        for e in executions
    ]


@router.delete("/{execution_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_execution(execution_id: str) -> None:
    """Delete an execution record."""
    if execution_id not in _executions:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Execution not found: {execution_id}",
        )

    del _executions[execution_id]


def create_app() -> FastAPI:
    """Create the FastAPI application."""
    app = FastAPI(
        title="Aperion Flow",
        description="The Conductor - Pipeline & Workflow Engine for Aperion Ecosystem",
        version="0.1.0",
    )

    app.include_router(router)

    @app.get("/healthz")
    async def healthz() -> dict[str, str]:
        return {"status": "ok"}

    @app.get("/readyz")
    async def readyz() -> dict[str, str]:
        return {"status": "ready"}

    return app


# Entry point
if __name__ == "__main__":
    import uvicorn

    app = create_app()
    uvicorn.run(app, host="0.0.0.0", port=8001)
