"""edge_functions domain — HTTP routes.

Three surfaces:
1. /api/function-library           — public read + admin write
2. /api/projects/{id}/functions    — project function management
3. /api/admin/runtime-versions     — platform runtime versioning (admin only)
"""
from __future__ import annotations

from typing import Optional
from uuid import UUID

from fastapi import APIRouter, Depends, Query
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.domain.admin.router import require_admin
from src.domain.auth.models import User
from src.domain.auth.router import get_current_user
from src.domain.edge_functions.repository import (
    FunctionLibraryRepository,
    FunctionLibraryVersionRepository,
    ProjectFunctionRepository,
    ProjectFunctionTokenRepository,
    ProjectFunctionVersionRepository,
    ProjectWebhookEndpointRepository,
    WorkerRuntimeVersionRepository,
)
from src.domain.edge_functions.schemas import (
    CreateFunctionRequest,
    CreateLibraryFunctionRequest,
    CreateLibraryVersionRequest,
    CreateRuntimeVersionRequest,
    CreateWebhookEndpointRequest,
    DeployFromLibraryRequest,
    DeprecateLibraryFunctionRequest,
    FunctionLibraryResponse,
    FunctionLibraryUpsertResult,
    FunctionLibraryVersionResponse,
    FunctionResponse,
    FunctionVersionResponse,
    LibraryManifestImportBody,
    ManifestDiffResponse,
    ManifestValidateResponse,
    PublishLibraryFunctionRequest,
    RestoreLibraryBody,
    RuntimeVersionResponse,
    UpdateFunctionCodeRequest,
    UpdateFunctionSecretsRequest,
    UpdateLibraryFunctionRequest,
    UpdateRuntimeVersionRequest,
    WebhookEndpointResponse,
)
from src.domain.connectors.repository import ConnectorRepository
from src.domain.edge_functions.service import EdgeFunctionsService
from src.domain.projects.repository import ProjectRepository
from src.integrations.cloud_control_client import CloudControlClient


def _get_service(db: AsyncSession = Depends(get_db)) -> EdgeFunctionsService:
    return EdgeFunctionsService(
        library_repo=FunctionLibraryRepository(db),
        library_version_repo=FunctionLibraryVersionRepository(db),
        fn_repo=ProjectFunctionRepository(db),
        version_repo=ProjectFunctionVersionRepository(db),
        webhook_repo=ProjectWebhookEndpointRepository(db),
        runtime_repo=WorkerRuntimeVersionRepository(db),
        token_repo=ProjectFunctionTokenRepository(db),
        project_repo=ProjectRepository(db),
        connector_repo=ConnectorRepository(db),
        cloud_client=CloudControlClient(),
    )


# ---------------------------------------------------------------------------
# Function Library — public read, admin write
# ---------------------------------------------------------------------------

library_router = APIRouter(prefix="/api/function-library", tags=["function-library"])

admin_function_library_router = APIRouter(
    prefix="/api/admin/function-library", tags=["admin", "function-library"]
)


@admin_function_library_router.post(
    "/import", response_model=FunctionLibraryUpsertResult, status_code=201
)
async def import_library_manifest(
    body: LibraryManifestImportBody,
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionLibraryUpsertResult:
    return await svc.import_library_from_manifest_dict(
        body.manifest, source="api", publish=False
    )


@admin_function_library_router.post("/validate", response_model=ManifestValidateResponse)
async def validate_library_manifest(
    body: LibraryManifestImportBody,
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> ManifestValidateResponse:
    return svc.validate_library_manifest_dict(body.manifest)


@admin_function_library_router.post(
    "/{slug}/sync-from-manifest", response_model=FunctionLibraryUpsertResult
)
async def sync_library_from_manifest(
    slug: str,
    body: LibraryManifestImportBody,
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionLibraryUpsertResult:
    return await svc.sync_library_from_manifest_for_slug(slug, body.manifest)


@admin_function_library_router.get(
    "/{slug}/diff", response_model=ManifestDiffResponse
)
async def get_library_manifest_diff(
    slug: str,
    source: str = Query("seed", description="Use source=seed to diff DB vs seeds/function_library/{slug}.yaml"),
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> ManifestDiffResponse:
    if source != "seed":
        return ManifestDiffResponse(
            slug=slug,
            status="error",
            message="Only source=seed is supported for this endpoint",
        )
    return await svc.diff_manifest_against_seed(slug)


@admin_function_library_router.post(
    "/{slug}/versions/{version_id}/promote", response_model=FunctionLibraryResponse
)
async def promote_library_version(
    slug: str,
    version_id: UUID,
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionLibraryResponse:
    return await svc.promote_library_version(slug, version_id)


@admin_function_library_router.post(
    "/{slug}/disable", response_model=FunctionLibraryResponse
)
async def admin_disable_library_function(
    slug: str,
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionLibraryResponse:
    return await svc.disable_library_function(slug)


@admin_function_library_router.post(
    "/{slug}/restore", response_model=FunctionLibraryResponse
)
async def admin_restore_library_function(
    slug: str,
    body: RestoreLibraryBody = RestoreLibraryBody(),
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionLibraryResponse:
    return await svc.restore_library_function(slug, to_status=body.to_status)


@library_router.get("", response_model=list[FunctionLibraryResponse])
async def list_library_functions(
    trigger_type: Optional[str] = Query(None),
    tags: Optional[str] = Query(None, description="Comma-separated tags"),
    integration: Optional[str] = Query(None),
    category: Optional[str] = Query(None),
    minimum_plan: Optional[str] = Query(None),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> list[FunctionLibraryResponse]:
    tag_list = [t.strip() for t in tags.split(",")] if tags else None
    return await svc.list_library(
        trigger_type=trigger_type,
        tags=tag_list,
        integration=integration,
        category=category,
        minimum_plan=minimum_plan,
    )


@library_router.get("/{slug}", response_model=FunctionLibraryResponse)
async def get_library_function(
    slug: str,
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionLibraryResponse:
    return await svc.get_library_function(slug)


@library_router.post("", response_model=FunctionLibraryResponse, status_code=201)
async def create_library_function(
    body: CreateLibraryFunctionRequest,
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionLibraryResponse:
    return await svc.create_library_function(body)


@library_router.get("/admin/all", response_model=list[FunctionLibraryResponse])
async def list_all_library_functions(
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> list[FunctionLibraryResponse]:
    """Admin view — all entries regardless of status."""
    return await svc.list_library_all()


@library_router.patch("/{slug}", response_model=FunctionLibraryResponse)
async def update_library_function(
    slug: str,
    body: UpdateLibraryFunctionRequest,
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionLibraryResponse:
    return await svc.update_library_function(slug, body)


@library_router.post("/{slug}/publish", response_model=FunctionLibraryResponse)
async def publish_library_function(
    slug: str,
    body: PublishLibraryFunctionRequest,
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionLibraryResponse:
    return await svc.publish_library_function(slug, body)


@library_router.post("/{slug}/deprecate", response_model=FunctionLibraryResponse)
async def deprecate_library_function(
    slug: str,
    body: DeprecateLibraryFunctionRequest,
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionLibraryResponse:
    return await svc.deprecate_library_function(slug, body)


@library_router.get("/{slug}/versions", response_model=list[FunctionLibraryVersionResponse])
async def list_library_versions(
    slug: str,
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> list[FunctionLibraryVersionResponse]:
    return await svc.list_library_versions(slug)


@library_router.post(
    "/{slug}/versions",
    response_model=FunctionLibraryVersionResponse,
    status_code=201,
)
async def create_library_version(
    slug: str,
    body: CreateLibraryVersionRequest,
    admin: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionLibraryVersionResponse:
    return await svc.create_library_version(slug, body)


# ---------------------------------------------------------------------------
# Project Functions
# ---------------------------------------------------------------------------

functions_router = APIRouter(
    prefix="/api/projects/{project_id}/functions", tags=["edge-functions"]
)


@functions_router.get("", response_model=list[FunctionResponse])
async def list_functions(
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> list[FunctionResponse]:
    return await svc.list_functions(project_id)


@functions_router.post("", response_model=FunctionResponse, status_code=201)
async def create_function(
    body: CreateFunctionRequest,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionResponse:
    """Create and immediately deploy a custom function."""
    return await svc.create_function(project_id, body)


@functions_router.post("/deploy-from-library", response_model=FunctionResponse, status_code=201)
async def deploy_from_library(
    body: DeployFromLibraryRequest,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionResponse:
    """Deploy a pre-built library function to this project."""
    return await svc.deploy_from_library(project_id, body)


@functions_router.get("/{slug}", response_model=FunctionResponse)
async def get_function(
    slug: str,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionResponse:
    return await svc.get_function(project_id, slug)


@functions_router.put("/{slug}/code", response_model=FunctionVersionResponse)
async def update_function_code(
    slug: str,
    body: UpdateFunctionCodeRequest,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionVersionResponse:
    """Upload new code (creates a pending version — call /deploy to activate)."""
    return await svc.update_code(project_id, slug, body)


@functions_router.post("/{slug}/deploy", response_model=FunctionResponse)
async def deploy_function(
    slug: str,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionResponse:
    """Deploy the latest pending version of a function."""
    return await svc.deploy_version(project_id, slug)


@functions_router.post("/{slug}/rollback", response_model=FunctionResponse)
async def rollback_function(
    slug: str,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionResponse:
    """Roll back to the previous deployed version."""
    return await svc.rollback(project_id, slug)


@functions_router.put("/{slug}/secrets", response_model=FunctionResponse)
async def update_function_secrets(
    slug: str,
    body: UpdateFunctionSecretsRequest,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> FunctionResponse:
    return await svc.update_secrets(project_id, slug, body)


@functions_router.delete("/{slug}", status_code=204, response_model=None)
async def delete_function(
    slug: str,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> None:
    await svc.delete_function(project_id, slug)


# ---------------------------------------------------------------------------
# Webhook Endpoints
# ---------------------------------------------------------------------------

webhooks_router = APIRouter(
    prefix="/api/projects/{project_id}/webhooks", tags=["edge-functions"]
)


@webhooks_router.get("", response_model=list[WebhookEndpointResponse])
async def list_webhooks(
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> list[WebhookEndpointResponse]:
    return await svc.list_webhooks(project_id)


@webhooks_router.post("", response_model=WebhookEndpointResponse, status_code=201)
async def create_webhook(
    body: CreateWebhookEndpointRequest,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    _: User = Depends(get_current_user),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> WebhookEndpointResponse:
    """Manually register a webhook endpoint for a provider."""
    return await svc.create_webhook(project_id, body)


# ---------------------------------------------------------------------------
# Runtime Versions — admin only
# ---------------------------------------------------------------------------

runtime_router = APIRouter(prefix="/api/admin/runtime-versions", tags=["admin", "edge-functions"])


@runtime_router.get("", response_model=list[RuntimeVersionResponse])
async def list_runtime_versions(
    _: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> list[RuntimeVersionResponse]:
    return await svc.list_runtime_versions()


@runtime_router.post("", response_model=RuntimeVersionResponse, status_code=201)
async def create_runtime_version(
    body: CreateRuntimeVersionRequest,
    _: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> RuntimeVersionResponse:
    return await svc.create_runtime_version(body)


@runtime_router.patch("/{version}", response_model=RuntimeVersionResponse)
async def update_runtime_version(
    version: str,
    body: UpdateRuntimeVersionRequest,
    _: User = Depends(require_admin),
    svc: EdgeFunctionsService = Depends(_get_service),
) -> RuntimeVersionResponse:
    return await svc.update_runtime_version(version, body)
