# ================================================================
# services/forecasting/app/api/validation.py
# ================================================================
"""
Validation API - Forecast validation endpoints
"""

from fastapi import APIRouter, Depends, HTTPException, Path, Query, status
from typing import Dict, Any, List, Optional
from uuid import UUID
from datetime import datetime, timedelta, timezone
import structlog

from pydantic import BaseModel, Field
from app.services.validation_service import ValidationService
from shared.auth.decorators import get_current_user_dep
from shared.auth.access_control import require_user_role
from shared.routing import RouteBuilder
from app.core.database import get_db
from sqlalchemy.ext.asyncio import AsyncSession

route_builder = RouteBuilder('forecasting')
router = APIRouter(tags=["validation"])
logger = structlog.get_logger()


# ================================================================
# Request/Response Schemas
# ================================================================

class ValidationRequest(BaseModel):
    """Request model for validation"""
    start_date: datetime = Field(..., description="Start date for validation period")
    end_date: datetime = Field(..., description="End date for validation period")
    orchestration_run_id: Optional[UUID] = Field(None, description="Optional orchestration run ID")
    triggered_by: str = Field(default="manual", description="Trigger source")


class ValidationResponse(BaseModel):
    """Response model for validation results"""
    validation_run_id: str
    status: str
    forecasts_evaluated: int
    forecasts_with_actuals: int
    forecasts_without_actuals: int
    metrics_created: int
    overall_metrics: Optional[Dict[str, float]] = None
    total_predicted_demand: Optional[float] = None
    total_actual_demand: Optional[float] = None
    duration_seconds: Optional[float] = None
    message: Optional[str] = None


class ValidationRunResponse(BaseModel):
    """Response model for validation run details"""
    id: str
    tenant_id: str
    orchestration_run_id: Optional[str]
    validation_start_date: str
    validation_end_date: str
    started_at: str
    completed_at: Optional[str]
    duration_seconds: Optional[float]
    status: str
    total_forecasts_evaluated: int
    forecasts_with_actuals: int
    forecasts_without_actuals: int
    overall_mae: Optional[float]
    overall_mape: Optional[float]
    overall_rmse: Optional[float]
    overall_r2_score: Optional[float]
    overall_accuracy_percentage: Optional[float]
    total_predicted_demand: float
    total_actual_demand: float
    metrics_by_product: Optional[Dict[str, Any]]
    metrics_by_location: Optional[Dict[str, Any]]
    metrics_records_created: int
    error_message: Optional[str]
    triggered_by: str
    execution_mode: str


class AccuracyTrendResponse(BaseModel):
    """Response model for accuracy trends"""
    period_days: int
    total_runs: int
    average_mape: Optional[float]
    average_accuracy: Optional[float]
    trends: List[Dict[str, Any]]


# ================================================================
# Endpoints
# ================================================================

@router.post(
    route_builder.build_base_route("validation/validate-date-range"),
    response_model=ValidationResponse,
    status_code=status.HTTP_200_OK
)
@require_user_role(['admin', 'owner', 'member'])
async def validate_date_range(
    validation_request: ValidationRequest,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Validate forecasts against actual sales for a date range

    This endpoint:
    - Fetches forecasts for the specified date range
    - Retrieves corresponding actual sales data
    - Calculates accuracy metrics (MAE, MAPE, RMSE, R², accuracy %)
    - Stores performance metrics in the database
    - Returns validation summary
    """
    try:
        logger.info(
            "Starting date range validation",
            tenant_id=tenant_id,
            start_date=validation_request.start_date.isoformat(),
            end_date=validation_request.end_date.isoformat(),
            user_id=current_user.get("user_id")
        )

        validation_service = ValidationService(db)

        result = await validation_service.validate_date_range(
            tenant_id=tenant_id,
            start_date=validation_request.start_date,
            end_date=validation_request.end_date,
            orchestration_run_id=validation_request.orchestration_run_id,
            triggered_by=validation_request.triggered_by
        )

        logger.info(
            "Date range validation completed",
            tenant_id=tenant_id,
            validation_run_id=result.get("validation_run_id"),
            forecasts_evaluated=result.get("forecasts_evaluated")
        )

        return ValidationResponse(**result)

    except Exception as e:
        logger.error(
            "Failed to validate date range",
            tenant_id=tenant_id,
            error=str(e),
            error_type=type(e).__name__
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to validate forecasts: {str(e)}"
        )


@router.post(
    route_builder.build_base_route("validation/validate-yesterday"),
    response_model=ValidationResponse,
    status_code=status.HTTP_200_OK
)
@require_user_role(['admin', 'owner', 'member'])
async def validate_yesterday(
    tenant_id: UUID = Path(..., description="Tenant ID"),
    orchestration_run_id: Optional[UUID] = Query(None, description="Optional orchestration run ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Validate yesterday's forecasts against actual sales

    Convenience endpoint for validating the most recent day's forecasts.
    This is typically called by the orchestrator as part of the daily workflow.
    """
    try:
        logger.info(
            "Starting yesterday validation",
            tenant_id=tenant_id,
            user_id=current_user.get("user_id")
        )

        validation_service = ValidationService(db)

        result = await validation_service.validate_yesterday(
            tenant_id=tenant_id,
            orchestration_run_id=orchestration_run_id,
            triggered_by="manual"
        )

        logger.info(
            "Yesterday validation completed",
            tenant_id=tenant_id,
            validation_run_id=result.get("validation_run_id"),
            forecasts_evaluated=result.get("forecasts_evaluated")
        )

        return ValidationResponse(**result)

    except Exception as e:
        logger.error(
            "Failed to validate yesterday",
            tenant_id=tenant_id,
            error=str(e),
            error_type=type(e).__name__
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to validate yesterday's forecasts: {str(e)}"
        )


@router.get(
    route_builder.build_base_route("validation/runs/{validation_run_id}"),
    response_model=ValidationRunResponse,
    status_code=status.HTTP_200_OK
)
@require_user_role(['admin', 'owner', 'member'])
async def get_validation_run(
    validation_run_id: UUID = Path(..., description="Validation run ID"),
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Get details of a specific validation run

    Returns complete information about a validation execution including:
    - Summary statistics
    - Overall accuracy metrics
    - Breakdown by product and location
    - Execution metadata
    """
    try:
        validation_service = ValidationService(db)

        validation_run = await validation_service.get_validation_run(validation_run_id)

        if not validation_run:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Validation run {validation_run_id} not found"
            )

        if validation_run.tenant_id != tenant_id:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Access denied to this validation run"
            )

        return ValidationRunResponse(**validation_run.to_dict())

    except HTTPException:
        raise
    except Exception as e:
        logger.error(
            "Failed to get validation run",
            validation_run_id=validation_run_id,
            error=str(e)
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to get validation run: {str(e)}"
        )


@router.get(
    route_builder.build_base_route("validation/runs"),
    response_model=List[ValidationRunResponse],
    status_code=status.HTTP_200_OK
)
@require_user_role(['admin', 'owner', 'member'])
async def get_validation_runs(
    tenant_id: UUID = Path(..., description="Tenant ID"),
    limit: int = Query(50, ge=1, le=100, description="Number of records to return"),
    skip: int = Query(0, ge=0, description="Number of records to skip"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Get validation runs for a tenant

    Returns a list of validation executions with pagination support.
    """
    try:
        validation_service = ValidationService(db)

        runs = await validation_service.get_validation_runs_by_tenant(
            tenant_id=tenant_id,
            limit=limit,
            skip=skip
        )

        return [ValidationRunResponse(**run.to_dict()) for run in runs]

    except Exception as e:
        logger.error(
            "Failed to get validation runs",
            tenant_id=tenant_id,
            error=str(e)
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to get validation runs: {str(e)}"
        )


@router.get(
    route_builder.build_base_route("validation/trends"),
    response_model=AccuracyTrendResponse,
    status_code=status.HTTP_200_OK
)
@require_user_role(['admin', 'owner', 'member'])
async def get_accuracy_trends(
    tenant_id: UUID = Path(..., description="Tenant ID"),
    days: int = Query(30, ge=1, le=365, description="Number of days to analyze"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Get accuracy trends over time

    Returns validation accuracy metrics over the specified time period.
    Useful for monitoring model performance degradation and improvement.
    """
    try:
        validation_service = ValidationService(db)

        trends = await validation_service.get_accuracy_trends(
            tenant_id=tenant_id,
            days=days
        )

        return AccuracyTrendResponse(**trends)

    except Exception as e:
        logger.error(
            "Failed to get accuracy trends",
            tenant_id=tenant_id,
            error=str(e)
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to get accuracy trends: {str(e)}"
        )
