"""HTTP routes for file-upload project imports.

Two-step flow:
1. POST /upload-url   → presigned R2 URL(s); client uploads directly to R2.
2. POST /upload-complete → complete multipart upload (multipart only).

Project creation itself happens through the normal projects API with
source_kind="file_upload" and the r2_key returned here.
"""
from __future__ import annotations

from typing import Any
from uuid import UUID

from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession

from src.config import get_settings
from src.core.database import get_db
from src.core.dependencies import require_active_billing_for_workspace
from src.core.exceptions import ForbiddenError, ValidationError
from src.domain.auth.models import User
from src.domain.auth.router import get_current_user
from src.domain.workspaces.facades import WorkspaceFacadeImpl
from src.domain.workspaces.repository import MembershipRepository, WorkspaceRepository
from src.integrations.r2_import_client import (
    MULTIPART_MAX_BYTES,
    SINGLE_PART_MAX_BYTES,
    abort_multipart_upload,
    complete_multipart_upload,
    create_multipart_upload,
    generate_single_part_upload_url,
)

router = APIRouter(prefix="/api/workspaces", tags=["project-imports"])


def _facade(db: AsyncSession) -> WorkspaceFacadeImpl:
    return WorkspaceFacadeImpl(WorkspaceRepository(db), MembershipRepository(db))


class UploadUrlRequest(BaseModel):
    filename: str = Field(..., min_length=1, max_length=255,
                          description="Original filename including extension (.zip, .tar.gz, .bundle)")
    file_size: int = Field(..., gt=0, description="File size in bytes")


class MultipartCompleteRequest(BaseModel):
    r2_key: str
    upload_id: str
    parts: list[dict[str, Any]] = Field(..., description="List of {part_number, etag}")


@router.post("/{ws}/project-imports/upload-url")
async def get_import_upload_url(
    body: UploadUrlRequest,
    ws: UUID = Depends(require_active_billing_for_workspace),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    facade = _facade(db)
    if not await facade.check_permission(ws, user.id, "projects.create"):
        raise ForbiddenError()
    settings = get_settings()
    if not settings.r2_imports_bucket or not settings.r2_imports_access_key_id:
        raise ValidationError("File upload imports are not configured on this instance.")
    if body.file_size > MULTIPART_MAX_BYTES:
        raise ValidationError(f"File size exceeds the 20 GB limit ({body.file_size} bytes).")
    import asyncio
    if body.file_size <= SINGLE_PART_MAX_BYTES:
        result = await asyncio.to_thread(
            generate_single_part_upload_url, settings, filename=body.filename
        )
    else:
        result = await asyncio.to_thread(
            create_multipart_upload, settings, filename=body.filename
        )
    return result


@router.post("/{ws}/project-imports/upload-complete")
async def complete_import_upload(
    body: MultipartCompleteRequest,
    ws: UUID = Depends(require_active_billing_for_workspace),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    facade = _facade(db)
    if not await facade.check_permission(ws, user.id, "projects.create"):
        raise ForbiddenError()
    settings = get_settings()
    if not settings.r2_imports_bucket or not settings.r2_imports_access_key_id:
        raise ValidationError("File upload imports are not configured on this instance.")
    import asyncio
    try:
        await asyncio.to_thread(
            complete_multipart_upload,
            settings,
            r2_key=body.r2_key,
            upload_id=body.upload_id,
            parts=body.parts,
        )
    except Exception as exc:
        await asyncio.to_thread(
            abort_multipart_upload,
            settings,
            r2_key=body.r2_key,
            upload_id=body.upload_id,
        )
        raise ValidationError(f"Failed to complete multipart upload: {exc}") from exc
    return {"ok": True, "r2_key": body.r2_key}
