from __future__ import annotations

from typing import Any

from fastapi import APIRouter, Depends, HTTPException, Query, Response, Security, status
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.database.crud.webhook import (
    create_webhook,
    delete_webhook,
    get_webhook_by_id,
    list_webhooks,
    update_webhook,
)
from app.database.models import Webhook, WebhookDelivery

from ..dependencies import get_db_session, require_api_token
from ..schemas.webhooks import (
    WebhookCreateRequest,
    WebhookDeliveryListResponse,
    WebhookDeliveryResponse,
    WebhookListResponse,
    WebhookResponse,
    WebhookStatsResponse,
    WebhookUpdateRequest,
)


router = APIRouter()


def _serialize_webhook(webhook: Webhook) -> WebhookResponse:
    return WebhookResponse(
        id=webhook.id,
        name=webhook.name,
        url=webhook.url,
        event_type=webhook.event_type,
        is_active=webhook.is_active,
        description=webhook.description,
        created_at=webhook.created_at,
        updated_at=webhook.updated_at,
        last_triggered_at=webhook.last_triggered_at,
        failure_count=webhook.failure_count,
        success_count=webhook.success_count,
    )


def _serialize_delivery(delivery: WebhookDelivery) -> WebhookDeliveryResponse:
    return WebhookDeliveryResponse(
        id=delivery.id,
        webhook_id=delivery.webhook_id,
        event_type=delivery.event_type,
        payload=delivery.payload,
        response_status=delivery.response_status,
        response_body=delivery.response_body,
        status=delivery.status,
        error_message=delivery.error_message,
        attempt_number=delivery.attempt_number,
        created_at=delivery.created_at,
        delivered_at=delivery.delivered_at,
        next_retry_at=delivery.next_retry_at,
    )


@router.get('', response_model=WebhookListResponse)
async def list_webhooks_endpoint(
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    event_type: str | None = Query(default=None),
    is_active: bool | None = Query(default=None),
) -> WebhookListResponse:
    """Список webhooks."""
    webhooks, total = await list_webhooks(
        db,
        event_type=event_type,
        is_active=is_active,
        limit=limit,
        offset=offset,
    )

    return WebhookListResponse(
        items=[_serialize_webhook(webhook) for webhook in webhooks],
        total=total,
        limit=limit,
        offset=offset,
    )


@router.get('/stats', response_model=WebhookStatsResponse)
async def get_webhook_stats(
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> WebhookStatsResponse:
    """Статистика по webhooks."""
    total_webhooks = await db.scalar(select(func.count(Webhook.id))) or 0
    active_webhooks = await db.scalar(select(func.count(Webhook.id)).where(Webhook.is_active == True)) or 0

    total_deliveries = await db.scalar(select(func.count(WebhookDelivery.id))) or 0
    successful_deliveries = (
        await db.scalar(select(func.count(WebhookDelivery.id)).where(WebhookDelivery.status == 'success')) or 0
    )
    failed_deliveries = (
        await db.scalar(select(func.count(WebhookDelivery.id)).where(WebhookDelivery.status == 'failed')) or 0
    )

    success_rate = (successful_deliveries / total_deliveries * 100) if total_deliveries > 0 else 0.0

    return WebhookStatsResponse(
        total_webhooks=int(total_webhooks),
        active_webhooks=int(active_webhooks),
        total_deliveries=int(total_deliveries),
        successful_deliveries=int(successful_deliveries),
        failed_deliveries=int(failed_deliveries),
        success_rate=round(success_rate, 2),
    )


@router.get('/{webhook_id}', response_model=WebhookResponse)
async def get_webhook(
    webhook_id: int,
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> WebhookResponse:
    """Получить webhook по ID."""
    webhook = await get_webhook_by_id(db, webhook_id)
    if not webhook:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'Webhook not found')
    return _serialize_webhook(webhook)


@router.post('', response_model=WebhookResponse, status_code=status.HTTP_201_CREATED)
async def create_webhook_endpoint(
    payload: WebhookCreateRequest,
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> WebhookResponse:
    """Создать новый webhook."""
    webhook = await create_webhook(
        db,
        name=payload.name,
        url=payload.url,
        event_type=payload.event_type,
        secret=payload.secret,
        description=payload.description,
    )
    return _serialize_webhook(webhook)


@router.patch('/{webhook_id}', response_model=WebhookResponse)
async def update_webhook_endpoint(
    webhook_id: int,
    payload: WebhookUpdateRequest,
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> WebhookResponse:
    """Обновить webhook."""
    webhook = await get_webhook_by_id(db, webhook_id)
    if not webhook:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'Webhook not found')

    webhook = await update_webhook(
        db,
        webhook,
        name=payload.name,
        url=payload.url,
        secret=payload.secret,
        description=payload.description,
        is_active=payload.is_active,
    )
    return _serialize_webhook(webhook)


@router.delete('/{webhook_id}', status_code=status.HTTP_204_NO_CONTENT)
async def delete_webhook_endpoint(
    webhook_id: int,
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
) -> Response:
    """Удалить webhook."""
    webhook = await get_webhook_by_id(db, webhook_id)
    if not webhook:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'Webhook not found')

    await delete_webhook(db, webhook)
    return Response(status_code=status.HTTP_204_NO_CONTENT)


@router.get('/{webhook_id}/deliveries', response_model=WebhookDeliveryListResponse)
async def list_webhook_deliveries(
    webhook_id: int,
    _: Any = Security(require_api_token),
    db: AsyncSession = Depends(get_db_session),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    status_filter: str | None = Query(default=None, alias='status'),
) -> WebhookDeliveryListResponse:
    """Список доставок webhook."""
    webhook = await get_webhook_by_id(db, webhook_id)
    if not webhook:
        raise HTTPException(status.HTTP_404_NOT_FOUND, 'Webhook not found')

    query = select(WebhookDelivery).where(WebhookDelivery.webhook_id == webhook_id)

    if status_filter:
        query = query.where(WebhookDelivery.status == status_filter)

    # Подсчет общего количества
    count_query = select(func.count()).select_from(query.subquery())
    total = await db.scalar(count_query) or 0

    # Получение данных
    query = query.order_by(WebhookDelivery.created_at.desc()).offset(offset).limit(limit)
    result = await db.execute(query)
    deliveries = result.scalars().all()

    return WebhookDeliveryListResponse(
        items=[_serialize_delivery(delivery) for delivery in deliveries],
        total=int(total),
        limit=limit,
        offset=offset,
    )
