"""
Webhook Management API Routes
Allows clients to manage their webhook configurations
"""
from datetime import datetime
from typing import List, Optional, Dict, Any
from fastapi import APIRouter, Depends, HTTPException, status, Query
from pydantic import BaseModel, HttpUrl, validator
from sqlalchemy.ext.asyncio import AsyncSession

from app.database import get_db
from app.auth.jwt_handler import get_current_user
from app.models.user import User
from app.models.webhook import WebhookEventType
from app.services.webhook_service import webhook_service

router = APIRouter()


# ============== Schemas ==============

class WebhookCreate(BaseModel):
    name: str
    url: HttpUrl
    events: List[str]
    description: Optional[str] = None
    custom_headers: Optional[Dict[str, str]] = None
    max_retries: int = 3
    timeout_seconds: int = 30

    @validator('name')
    def validate_name(cls, v):
        if len(v) < 3 or len(v) > 100:
            raise ValueError('Name must be between 3 and 100 characters')
        return v

    @validator('events')
    def validate_events(cls, v):
        if not v:
            raise ValueError('At least one event type is required')
        valid_events = [e.value for e in WebhookEventType]
        for event in v:
            if event not in valid_events:
                raise ValueError(f'Invalid event type: {event}. Valid types: {valid_events}')
        return v

    @validator('max_retries')
    def validate_max_retries(cls, v):
        if v < 0 or v > 10:
            raise ValueError('max_retries must be between 0 and 10')
        return v

    @validator('timeout_seconds')
    def validate_timeout(cls, v):
        if v < 5 or v > 60:
            raise ValueError('timeout_seconds must be between 5 and 60')
        return v


class WebhookUpdate(BaseModel):
    name: Optional[str] = None
    url: Optional[HttpUrl] = None
    events: Optional[List[str]] = None
    description: Optional[str] = None
    custom_headers: Optional[Dict[str, str]] = None
    is_active: Optional[bool] = None
    max_retries: Optional[int] = None
    timeout_seconds: Optional[int] = None


class WebhookResponse(BaseModel):
    id: int
    name: str
    description: Optional[str]
    url: str
    events: List[str]
    is_active: bool
    max_retries: int
    timeout_seconds: int
    total_deliveries: int
    successful_deliveries: int
    failed_deliveries: int
    last_delivery_at: Optional[datetime]
    last_success_at: Optional[datetime]
    created_at: datetime

    class Config:
        from_attributes = True


class WebhookWithSecretResponse(WebhookResponse):
    secret_key: str


class DeliveryLogResponse(BaseModel):
    id: int
    event_type: str
    event_id: str
    status: str
    attempt_count: int
    response_status_code: Optional[int]
    response_time_ms: Optional[int]
    error_message: Optional[str]
    delivered_at: Optional[datetime]
    created_at: datetime

    class Config:
        from_attributes = True


class WebhookEventTypesResponse(BaseModel):
    events: List[Dict[str, str]]


class TestWebhookResponse(BaseModel):
    success: bool
    status_code: Optional[int] = None
    response_body: Optional[str] = None
    error: Optional[str] = None
    event_id: str


# ============== Routes ==============

@router.get("/events", response_model=WebhookEventTypesResponse)
async def list_event_types():
    """
    List all available webhook event types
    """
    events = []
    categories = {
        "conversation": "Conversation Events",
        "invoice": "Invoice Events",
        "payment": "Payment Events",
        "subscription": "Subscription Events",
        "usage": "Usage Events",
        "api_key": "API Key Events"
    }

    for event_type in WebhookEventType:
        category = event_type.value.split(".")[0]
        events.append({
            "type": event_type.value,
            "category": categories.get(category, "Other"),
            "description": event_type.value.replace(".", " ").replace("_", " ").title()
        })

    return {"events": events}


@router.post("", response_model=WebhookWithSecretResponse, status_code=status.HTTP_201_CREATED)
async def create_webhook(
    webhook_data: WebhookCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    Create a new webhook endpoint

    The response includes the secret_key which is used to verify webhook signatures.
    Store this secret securely - it won't be shown again.
    """
    if not current_user.client_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="User must be associated with a client"
        )

    webhook = await webhook_service.create_webhook(
        db=db,
        client_id=current_user.client_id,
        name=webhook_data.name,
        url=str(webhook_data.url),
        events=webhook_data.events,
        description=webhook_data.description,
        custom_headers=webhook_data.custom_headers,
        max_retries=webhook_data.max_retries,
        timeout_seconds=webhook_data.timeout_seconds
    )

    return webhook


@router.get("", response_model=List[WebhookResponse])
async def list_webhooks(
    is_active: Optional[bool] = None,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    List all webhooks for the current client
    """
    if not current_user.client_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="User must be associated with a client"
        )

    webhooks = await webhook_service.get_client_webhooks(
        db=db,
        client_id=current_user.client_id,
        is_active=is_active
    )

    return webhooks


@router.get("/{webhook_id}", response_model=WebhookResponse)
async def get_webhook(
    webhook_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    Get webhook details by ID
    """
    if not current_user.client_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="User must be associated with a client"
        )

    webhook = await webhook_service.get_webhook(
        db=db,
        webhook_id=webhook_id,
        client_id=current_user.client_id
    )

    if not webhook:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Webhook not found"
        )

    return webhook


@router.patch("/{webhook_id}", response_model=WebhookResponse)
async def update_webhook(
    webhook_id: int,
    webhook_data: WebhookUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    Update webhook configuration
    """
    if not current_user.client_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="User must be associated with a client"
        )

    updates = webhook_data.dict(exclude_unset=True)
    if 'url' in updates and updates['url']:
        updates['url'] = str(updates['url'])

    webhook = await webhook_service.update_webhook(
        db=db,
        webhook_id=webhook_id,
        client_id=current_user.client_id,
        **updates
    )

    if not webhook:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Webhook not found"
        )

    return webhook


@router.delete("/{webhook_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_webhook(
    webhook_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    Delete a webhook
    """
    if not current_user.client_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="User must be associated with a client"
        )

    deleted = await webhook_service.delete_webhook(
        db=db,
        webhook_id=webhook_id,
        client_id=current_user.client_id
    )

    if not deleted:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Webhook not found"
        )


@router.post("/{webhook_id}/rotate-secret", response_model=Dict[str, str])
async def rotate_webhook_secret(
    webhook_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    Regenerate webhook secret

    Use this if your secret has been compromised.
    The old secret will immediately stop working.
    """
    if not current_user.client_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="User must be associated with a client"
        )

    new_secret = await webhook_service.regenerate_secret(
        db=db,
        webhook_id=webhook_id,
        client_id=current_user.client_id
    )

    if not new_secret:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Webhook not found"
        )

    return {"secret_key": new_secret}


@router.post("/{webhook_id}/test", response_model=TestWebhookResponse)
async def test_webhook(
    webhook_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    Send a test webhook to verify the endpoint is working
    """
    if not current_user.client_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="User must be associated with a client"
        )

    result = await webhook_service.test_webhook(
        db=db,
        webhook_id=webhook_id,
        client_id=current_user.client_id
    )

    return result


@router.get("/{webhook_id}/deliveries", response_model=List[DeliveryLogResponse])
async def get_delivery_logs(
    webhook_id: int,
    limit: int = Query(default=50, le=100),
    offset: int = Query(default=0, ge=0),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    Get delivery logs for a webhook
    """
    if not current_user.client_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="User must be associated with a client"
        )

    logs = await webhook_service.get_delivery_logs(
        db=db,
        webhook_id=webhook_id,
        client_id=current_user.client_id,
        limit=limit,
        offset=offset
    )

    # Convert enum status to string
    for log in logs:
        log.status = log.status.value if hasattr(log.status, 'value') else str(log.status)

    return logs
