"""HTTP routes for user Bitbucket OAuth, project import, and mirror targets."""
from __future__ import annotations

import urllib.parse
from typing import Optional
from uuid import UUID

from fastapi import APIRouter, Depends, Header, Path, Query, Request
from fastapi.responses import RedirectResponse
from sqlalchemy.ext.asyncio import AsyncSession

from src.config import get_settings
from src.core.app_url import resolve_bitbucket_user_mirror_return_url
from src.core.database import get_db
from src.core.dependencies import (
    require_active_billing_for_project_id,
    require_active_billing_for_workspace,
)
from src.core.exceptions import ForbiddenError, NotFoundError
from src.domain.auth.models import User
from src.domain.auth.router import get_current_user
from src.domain.bitbucket_user_mirror.repository import (
    ProjectUserBitbucketMirrorRepository,
    WorkspaceUserBitbucketConnectionRepository,
)
from src.domain.bitbucket_user_mirror.schemas import (
    BitbucketConnectUrlResponse,
    BitbucketRepoItem,
    BitbucketUserConnectionResponse,
    ProjectBitbucketMirrorResponse,
    ProjectBitbucketMirrorUpsert,
)
from src.domain.bitbucket_user_mirror.service import BitbucketUserMirrorService
from src.domain.bitbucket_user_mirror.verification import verify_bitbucket_user_webhook_signature
from src.domain.projects.repository import ProjectRepository
from src.domain.workspaces.facades import WorkspaceFacadeImpl
from src.domain.workspaces.repository import MembershipRepository, WorkspaceRepository

webhook_router = APIRouter(prefix="/api/webhooks", tags=["bitbucket-user-webhooks"])
integrations_router = APIRouter(prefix="/api/integrations", tags=["bitbucket-user-mirror"])
workspace_router = APIRouter(prefix="/api/workspaces", tags=["bitbucket-user-mirror"])
project_mirror_router = APIRouter(prefix="/api/projects", tags=["bitbucket-user-mirror"])


def _service(db: AsyncSession) -> BitbucketUserMirrorService:
    return BitbucketUserMirrorService(
        WorkspaceUserBitbucketConnectionRepository(db),
        ProjectUserBitbucketMirrorRepository(db),
        ProjectRepository(db),
    )


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


def _api_callback_url(request: Request) -> str:
    settings = get_settings()
    api_base = (settings.api_url or str(request.base_url).rstrip("/")).rstrip("/")
    return f"{api_base}/api/integrations/bitbucket-user/callback"


@webhook_router.post("/bitbucket-user")
async def bitbucket_user_webhook(
    request: Request,
    db: AsyncSession = Depends(get_db),
    x_event_key: Optional[str] = Header(None),
    x_hook_uuid: Optional[str] = Header(None),
    x_request_uuid: Optional[str] = Header(None),
    x_hub_signature: Optional[str] = Header(None),
    x_hub_signature_256: Optional[str] = Header(None),
):
    body = await request.body()
    signature = x_hub_signature_256 or x_hub_signature
    if not verify_bitbucket_user_webhook_signature(body, signature):
        raise ForbiddenError("Invalid webhook signature")
    payload = await request.json()
    from src.domain.bitbucket_user_mirror import webhook_handlers

    result = await webhook_handlers.dispatch(db, x_event_key or "", payload)
    await db.commit()
    return {
        "ok": True,
        "event": x_event_key or "",
        "delivery": x_request_uuid or x_hook_uuid or "",
        **result,
    }


@integrations_router.get("/bitbucket-user/callback")
async def bitbucket_user_oauth_callback(
    request: Request,
    code: str = Query(...),
    state: str = Query(...),
    db: AsyncSession = Depends(get_db),
):
    row, state_payload = await _service(db).exchange_callback(
        code=code,
        state=state,
        redirect_uri=_api_callback_url(request),
    )
    await db.commit()
    base = (
        state_payload.get("return_url") or resolve_bitbucket_user_mirror_return_url(request)
    ).rstrip("/")
    query = {"connection_id": str(row.id)}
    if state_payload.get("import_project_id"):
        query["project_id"] = str(state_payload["import_project_id"])
    sep = "&" if "?" in base else "?"
    return RedirectResponse(f"{base}{sep}{urllib.parse.urlencode(query)}", status_code=302)


@workspace_router.get(
    "/{ws}/bitbucket-user/connect-url",
    response_model=BitbucketConnectUrlResponse,
)
async def get_bitbucket_user_connect_url(
    request: Request,
    ws: UUID = Depends(require_active_billing_for_workspace),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
    redirect_base: Optional[str] = Query(None),
):
    facade = _facade(db)
    if not await facade.check_permission(ws, user.id, "repos.manage"):
        raise ForbiddenError()
    callback_return_url = resolve_bitbucket_user_mirror_return_url(
        request,
        redirect_base=redirect_base,
    )
    redirect_uri = _api_callback_url(request)
    connect_url, state = _service(db).build_connect_url(
        workspace_id=ws,
        user_id=user.id,
        callback_return_url=callback_return_url,
        redirect_uri=redirect_uri,
    )
    return BitbucketConnectUrlResponse(
        connect_url=connect_url,
        state=state,
        callback_return_url=callback_return_url,
        redirect_uri=redirect_uri,
    )


@workspace_router.get(
    "/{ws}/bitbucket-user/connections",
    response_model=list[BitbucketUserConnectionResponse],
)
async def list_bitbucket_user_connections(
    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, "repos.manage"):
        raise ForbiddenError()
    rows = await WorkspaceUserBitbucketConnectionRepository(db).list_for_workspace(ws)
    return [BitbucketUserConnectionResponse.model_validate(r) for r in rows]


@workspace_router.delete("/{ws}/bitbucket-user/connections/{connection_id}")
async def delete_bitbucket_user_connection(
    connection_id: UUID = Path(...),
    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, "repos.manage"):
        raise ForbiddenError()
    repo = WorkspaceUserBitbucketConnectionRepository(db)
    row = await repo.get_by_id(connection_id)
    if not row or row.workspace_id != ws:
        raise NotFoundError("Bitbucket connection not found")
    await _service(db).cleanup_webhooks_for_connection(connection_id)
    await repo.delete(connection_id)
    await db.commit()
    return {"ok": True}


@workspace_router.get(
    "/{ws}/bitbucket-user/connections/{connection_id}/repositories",
    response_model=list[BitbucketRepoItem],
)
async def list_bitbucket_user_repositories(
    connection_id: UUID = Path(...),
    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, "repos.manage"):
        raise ForbiddenError()
    raw = await _service(db).list_connection_repositories(ws, connection_id)
    return [BitbucketRepoItem.model_validate(x) for x in raw]


@project_mirror_router.put(
    "/{project_id}/bitbucket-user-mirror",
    response_model=ProjectBitbucketMirrorResponse,
)
async def upsert_project_bitbucket_mirror(
    body: ProjectBitbucketMirrorUpsert,
    project_id: UUID = Depends(require_active_billing_for_project_id),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    proj = await ProjectRepository(db).get_by_id(project_id)
    if not proj:
        raise NotFoundError("Project not found")
    facade = _facade(db)
    row = await _service(db).upsert_project_mirror(
        project_id=project_id,
        workspace_id=proj.workspace_id,
        user_id=user.id,
        connection_row_id=body.workspace_user_bitbucket_connection_id,
        target_repo_full_name=body.target_repo_full_name,
        target_default_branch=body.target_default_branch,
        enabled=body.enabled,
        check_permission=facade.check_permission,
    )
    await db.commit()
    return ProjectBitbucketMirrorResponse.model_validate(row)


@project_mirror_router.delete("/{project_id}/bitbucket-user-mirror")
async def delete_project_bitbucket_mirror(
    project_id: UUID = Depends(require_active_billing_for_project_id),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    proj = await ProjectRepository(db).get_by_id(project_id)
    if not proj:
        raise NotFoundError("Project not found")
    facade = _facade(db)
    deleted = await _service(db).delete_project_mirror(
        project_id=project_id,
        workspace_id=proj.workspace_id,
        user_id=user.id,
        check_permission=facade.check_permission,
    )
    if not deleted:
        raise NotFoundError("No Bitbucket mirror configured for this project")
    await db.commit()
    return {"ok": True}


@project_mirror_router.get(
    "/{project_id}/bitbucket-user-mirror",
    response_model=ProjectBitbucketMirrorResponse,
)
async def get_project_bitbucket_mirror(
    project_id: UUID = Depends(require_active_billing_for_project_id),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    proj = await ProjectRepository(db).get_by_id(project_id)
    if not proj:
        raise NotFoundError("Project not found")
    facade = _facade(db)
    if not await facade.check_permission(proj.workspace_id, user.id, "projects.read"):
        raise ForbiddenError()
    row = await ProjectUserBitbucketMirrorRepository(db).get_by_project_id(project_id)
    if not row:
        raise NotFoundError("No Bitbucket mirror configured for this project")
    return ProjectBitbucketMirrorResponse.model_validate(row)


router = APIRouter()
router.include_router(webhook_router)
router.include_router(integrations_router)
router.include_router(workspace_router)
router.include_router(project_mirror_router)
