"""HTTP routes for server-side storage ingest (upload → extract → R2)."""
from __future__ import annotations

from uuid import UUID

from fastapi import APIRouter, Depends, File, Form, Request, UploadFile
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.database import get_db
from src.core.dependencies import require_active_billing_for_project_id
from src.core.exceptions import NotFoundError, ValidationError
from src.domain.auth.models import User
from src.domain.auth.router import get_current_user
from src.domain.connectors.repository import ConnectorRepository
from src.domain.projects.repository import ProjectRepository
from src.domain.storage_ingest.repository import StorageIngestJobRepository
from src.domain.storage_ingest.schemas import (
    IngestUploadPartResponse,
    IngestUploadSessionResponse,
    StartIngestUploadSessionRequest,
    StorageIngestJobResponse,
    StorageIngestJobStatusResponse,
)
from src.domain.storage_ingest.service import StorageIngestService

router = APIRouter(prefix="/api/projects/{project_id}/storage", tags=["storage-ingest"])


def _service(db: AsyncSession = Depends(get_db)) -> StorageIngestService:
    return StorageIngestService(
        db=db,
        connector_repo=ConnectorRepository(db),
        job_repo=StorageIngestJobRepository(db),
    )


@router.post("/ingest-upload/sessions", response_model=IngestUploadSessionResponse, status_code=201)
async def start_ingest_upload_session(
    body: StartIngestUploadSessionRequest,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
    svc: StorageIngestService = Depends(_service),
) -> IngestUploadSessionResponse:
    """Start a chunked upload session (each part stays under Cloudflare's body limit)."""
    project = await ProjectRepository(db).get_by_id(project_id)
    if not project:
        raise NotFoundError("Project not found")
    return await svc.start_upload_session(project, body)


@router.put(
    "/ingest-upload/sessions/{job_id}/parts/{part_number}",
    response_model=IngestUploadPartResponse,
)
async def upload_ingest_part(
    job_id: UUID,
    part_number: int,
    request: Request,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: StorageIngestService = Depends(_service),
) -> IngestUploadPartResponse:
    chunk = await request.body()
    return await svc.append_upload_part(project_id, job_id, part_number, chunk)


@router.post(
    "/ingest-upload/sessions/{job_id}/complete",
    response_model=StorageIngestJobResponse,
    status_code=202,
)
async def complete_ingest_upload_session(
    job_id: UUID,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: StorageIngestService = Depends(_service),
) -> StorageIngestJobResponse:
    return await svc.complete_upload_session(project_id, job_id)


@router.post("/ingest-upload", response_model=StorageIngestJobResponse, status_code=202)
async def ingest_upload(
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
    svc: StorageIngestService = Depends(_service),
    file: UploadFile = File(...),
    bucket_name: str = Form(..., min_length=1, max_length=63),
    connection_id: UUID = Form(...),
    object_key: str | None = Form(None),
    extract_archive: str = Form("true"),
    archive_path_mode: str = Form("preserve_folders"),
) -> StorageIngestJobResponse:
    """One-shot upload for smaller files (under STORAGE_INGEST_SINGLE_POST_MAX_BYTES)."""
    extract_flag = extract_archive.strip().lower() in ("1", "true", "yes", "on")
    project = await ProjectRepository(db).get_by_id(project_id)
    if not project:
        raise NotFoundError("Project not found")
    return await svc.accept_upload(
        project,
        connection_id=connection_id,
        bucket_name=bucket_name,
        file=file,
        object_key=object_key,
        extract_archive=extract_flag,
        archive_path_mode=archive_path_mode,
    )


@router.get("/ingest-jobs/{job_id}", response_model=StorageIngestJobStatusResponse)
async def get_ingest_job(
    job_id: UUID,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: StorageIngestService = Depends(_service),
) -> StorageIngestJobStatusResponse:
    return await svc.get_job_status(project_id, job_id)


@router.post("/ingest-jobs/{job_id}/retry", response_model=StorageIngestJobResponse, status_code=202)
async def retry_ingest_job(
    job_id: UUID,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: StorageIngestService = Depends(_service),
) -> StorageIngestJobResponse:
    """Re-enqueue extract/R2 processing when a job is stuck in pending."""
    return await svc.retry_ingest_job(project_id, job_id)
