from datetime import datetime
from typing import Any, Optional, List
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session

from src.core.database import get_session
from src.apps.auth.utils.auth import get_current_merchant
from src.apps.developer import crud
from src.apps.developer.helpers.api_key_auth import generate_api_key, generate_key_id, generate_webhook_secret
from src.apps.developer.schemas.api_key_schemas import (
    CreateApiKeyRequest, ApiKeyCreatedResponse, ApiKeyResponse, UpdateApiKeyRequest
)
from src.apps.developer.schemas.webhook_schemas import (
    CreateWebhookRequest, WebhookCreatedResponse, WebhookResponse, UpdateWebhookRequest,
    WebhookDeliveryResponse, ApiRequestLogResponse, ApiRequestLogDetailResponse
)

router = APIRouter(prefix="/developer", tags=["Developer"])


# ─── API Key Endpoints ───────────────────────────────────────────────────────

@router.get("/api-keys", response_model=List[ApiKeyResponse])
async def list_api_keys(
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    keys = crud.get_api_keys_for_merchant(db, merchant.id)
    return keys


@router.post("/api-keys", response_model=ApiKeyCreatedResponse, status_code=status.HTTP_201_CREATED)
async def create_api_key(
    body: CreateApiKeyRequest,
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    raw_key, key_hash, key_salt, key_prefix = generate_api_key(body.environment.value)
    key_id = generate_key_id()

    key = crud.create_api_key(
        db,
        merchant_id=merchant.id,
        display_name=body.display_name,
        key_id=key_id,
        key_hash=key_hash,
        key_salt=key_salt,
        key_prefix=key_prefix,
        environment=body.environment.value,
        scopes=[s.value for s in body.scopes],
        allowed_ips=body.allowed_ips,
        expires_at=body.expires_at,
    )

    return ApiKeyCreatedResponse(
        id=key.id,
        key_id=key.key_id,
        api_key=raw_key,
        key_prefix=key.key_prefix,
        environment=key.environment,
        scopes=key.scopes,
        display_name=key.display_name,
        created_at=key.created_at,
    )


@router.get("/api-keys/{key_id}", response_model=ApiKeyResponse)
async def get_api_key(
    key_id: int,
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    key = crud.get_api_key_by_id(db, key_id, merchant.id)
    if not key:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="API key not found")
    return key


@router.put("/api-keys/{key_id}", response_model=ApiKeyResponse)
async def update_api_key(
    key_id: int,
    body: UpdateApiKeyRequest,
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    key = crud.get_api_key_by_id(db, key_id, merchant.id)
    if not key:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="API key not found")

    updates = body.model_dump(exclude_none=True)
    if "scopes" in updates:
        updates["scopes"] = [s.value if hasattr(s, "value") else s for s in updates["scopes"]]
    return crud.update_api_key(db, key, **updates)


@router.delete("/api-keys/{key_id}", status_code=status.HTTP_204_NO_CONTENT)
async def revoke_api_key(
    key_id: int,
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    key = crud.get_api_key_by_id(db, key_id, merchant.id)
    if not key:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="API key not found")
    crud.revoke_api_key(db, key)


# ─── Webhook Endpoints ───────────────────────────────────────────────────────

@router.get("/webhooks", response_model=List[WebhookResponse])
async def list_webhooks(
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    return crud.get_webhook_endpoints_for_merchant(db, merchant.id)


@router.post("/webhooks", response_model=WebhookCreatedResponse, status_code=status.HTTP_201_CREATED)
async def create_webhook(
    body: CreateWebhookRequest,
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    raw_secret, encrypted_secret = generate_webhook_secret()

    endpoint = crud.create_webhook_endpoint(
        db,
        merchant_id=merchant.id,
        display_name=body.display_name,
        url=body.url,
        events=body.events,
        signing_secret_encrypted=encrypted_secret,
    )

    return WebhookCreatedResponse(
        id=endpoint.id,
        display_name=endpoint.display_name,
        url=endpoint.url,
        events=endpoint.events,
        signing_secret=raw_secret,
        is_active=endpoint.is_active,
        created_at=endpoint.created_at,
    )


@router.put("/webhooks/{endpoint_id}", response_model=WebhookResponse)
async def update_webhook(
    endpoint_id: int,
    body: UpdateWebhookRequest,
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    endpoint = crud.get_webhook_endpoint_by_id(db, endpoint_id, merchant.id)
    if not endpoint:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Webhook endpoint not found")

    updates = body.model_dump(exclude_none=True)
    return crud.update_webhook_endpoint(db, endpoint, **updates)


@router.delete("/webhooks/{endpoint_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_webhook(
    endpoint_id: int,
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    endpoint = crud.get_webhook_endpoint_by_id(db, endpoint_id, merchant.id)
    if not endpoint:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Webhook endpoint not found")
    crud.delete_webhook_endpoint(db, endpoint)


@router.post("/webhooks/{endpoint_id}/rotate-secret", response_model=WebhookCreatedResponse)
async def rotate_webhook_secret(
    endpoint_id: int,
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    endpoint = crud.get_webhook_endpoint_by_id(db, endpoint_id, merchant.id)
    if not endpoint:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Webhook endpoint not found")

    raw_secret, encrypted_secret = generate_webhook_secret()
    endpoint = crud.update_webhook_endpoint(db, endpoint, signing_secret_encrypted=encrypted_secret)

    return WebhookCreatedResponse(
        id=endpoint.id,
        display_name=endpoint.display_name,
        url=endpoint.url,
        events=endpoint.events,
        signing_secret=raw_secret,
        is_active=endpoint.is_active,
        created_at=endpoint.created_at,
    )


@router.get("/webhooks/{endpoint_id}/deliveries", response_model=dict)
async def get_webhook_deliveries(
    endpoint_id: int,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    endpoint = crud.get_webhook_endpoint_by_id(db, endpoint_id, merchant.id)
    if not endpoint:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Webhook endpoint not found")

    deliveries, total = crud.get_webhook_deliveries(db, endpoint_id, merchant.id, page, page_size)
    return {
        "items": [WebhookDeliveryResponse.model_validate(d) for d in deliveries],
        "total": total,
        "page": page,
        "page_size": page_size,
    }


# ─── API Request Logs ─────────────────────────────────────────────────────────

@router.get("/logs", response_model=dict)
async def get_api_request_logs(
    api_key_id: Optional[int] = Query(None),
    status_code: Optional[int] = Query(None),
    method: Optional[str] = Query(None),
    date_from: Optional[datetime] = Query(None),
    date_to: Optional[datetime] = Query(None),
    page: int = Query(1, ge=1),
    page_size: int = Query(50, ge=1, le=200),
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    logs, total = crud.get_api_request_logs(
        db,
        merchant_id=merchant.id,
        api_key_id=api_key_id,
        status_code=status_code,
        method=method,
        date_from=date_from,
        date_to=date_to,
        page=page,
        page_size=page_size,
    )

    # Enrich logs with key_id string from MerchantApiKey
    from src.apps.developer.models.merchant_api_key import MerchantApiKey
    from sqlalchemy import select
    key_map = {}
    for log in logs:
        if log.api_key_id not in key_map:
            key_obj = db.execute(
                select(MerchantApiKey).where(MerchantApiKey.id == log.api_key_id)
            ).scalar_one_or_none()
            key_map[log.api_key_id] = key_obj.key_id if key_obj else str(log.api_key_id)

    return {
        "items": [
            ApiRequestLogResponse(
                id=log.id,
                key_id=key_map.get(log.api_key_id, str(log.api_key_id)),
                method=log.method,
                path=log.path,
                query_params=log.query_params,
                status_code=log.status_code,
                duration_ms=log.duration_ms,
                ip_address=log.ip_address,
                request_id=log.request_id,
                error_message=log.error_message,
                created_at=log.created_at,
            )
            for log in logs
        ],
        "total": total,
        "page": page,
        "page_size": page_size,
    }


@router.get("/logs/{log_id}", response_model=dict)
async def get_api_request_log_detail(
    log_id: int,
    merchant: Any = Depends(get_current_merchant),
    db: Session = Depends(get_session),
):
    """Return a single log entry enriched with related entity details resolved from the path."""
    import re
    from sqlalchemy import select
    from src.apps.developer.models.api_request_log import ApiRequestLog
    from src.apps.developer.models.merchant_api_key import MerchantApiKey

    log = db.execute(
        select(ApiRequestLog).where(
            ApiRequestLog.id == log_id,
            ApiRequestLog.merchant_id == merchant.id,
        )
    ).scalar_one_or_none()
    if not log:
        raise HTTPException(status_code=404, detail="Log not found.")

    key_obj = db.execute(
        select(MerchantApiKey).where(MerchantApiKey.id == log.api_key_id)
    ).scalar_one_or_none()
    key_id_str = key_obj.key_id if key_obj else str(log.api_key_id)

    detail = ApiRequestLogDetailResponse(
        id=log.id,
        key_id=key_id_str,
        method=log.method,
        path=log.path,
        query_params=log.query_params,
        status_code=log.status_code,
        duration_ms=log.duration_ms,
        ip_address=log.ip_address,
        request_id=log.request_id,
        error_message=log.error_message,
        created_at=log.created_at,
        request_body=log.request_body,
        response_body=log.response_body,
    )

    path = log.path

    # ── Resolve related entity from path ──────────────────────────────────────
    # /api/v1/transactions/{id}
    m = re.search(r"/transactions/(\d+)", path)
    if m:
        try:
            from src.apps.transactions.models.transaction import Transactions
            txn = db.execute(
                select(Transactions).where(
                    Transactions.id == int(m.group(1)),
                    Transactions.merchant_id == merchant.id,
                )
            ).scalar_one_or_none()
            if txn:
                detail.related_entity_type = "transaction"
                detail.related_entity_id = txn.id
                detail.transaction_amount = float(txn.txn_amount) / 100 if txn.txn_amount else None
                detail.transaction_status = str(txn.txn_status.name) if txn.txn_status else None
                detail.transaction_literal = txn.txn_literal or txn.txn_id
                if txn.customer_id:
                    from src.apps.customers.models.customer import Customer
                    c = db.execute(select(Customer).where(Customer.id == txn.customer_id)).scalar_one_or_none()
                    if c:
                        name_parts = [p for p in [c.first_name, c.last_name] if p]
                        detail.customer_name = " ".join(name_parts) if name_parts else None
                        detail.customer_email = c.email
        except Exception:
            pass

    # /api/v1/payment-requests/{id}
    if not detail.related_entity_type:
        m = re.search(r"/payment-requests/(\d+)", path)
        if m:
            try:
                from src.apps.payment_requests.models.payment_request import PaymentRequest
                pr = db.execute(
                    select(PaymentRequest).where(
                        PaymentRequest.id == int(m.group(1)),
                        PaymentRequest.merchant_id == merchant.id,
                    )
                ).scalar_one_or_none()
                if pr:
                    detail.related_entity_type = "payment_request"
                    detail.related_entity_id = pr.id
                    detail.payment_request_literal = pr.payment_request_literal
                    detail.payment_request_amount = float(pr.amount) / 100 if pr.amount else None
                    detail.payment_request_status = str(pr.status.name) if pr.status else None
                    if pr.customer_id:
                        from src.apps.customers.models.customer import Customer
                        c = db.execute(select(Customer).where(Customer.id == pr.customer_id)).scalar_one_or_none()
                        if c:
                            name_parts = [p for p in [c.first_name, c.last_name] if p]
                            detail.customer_name = " ".join(name_parts) if name_parts else None
                            detail.customer_email = c.email
            except Exception:
                pass

    # /api/v1/customers/{id}
    if not detail.related_entity_type:
        m = re.search(r"/customers/(\d+)", path)
        if m:
            try:
                from src.apps.customers.models.customer import Customer
                c = db.execute(
                    select(Customer).where(
                        Customer.id == int(m.group(1)),
                        Customer.merchant_id == merchant.id,
                    )
                ).scalar_one_or_none()
                if c:
                    detail.related_entity_type = "customer"
                    detail.related_entity_id = c.id
                    name_parts = [p for p in [c.first_name, c.last_name] if p]
                    detail.customer_name = " ".join(name_parts) if name_parts else None
                    detail.customer_email = c.email
            except Exception:
                pass

    return detail.model_dump()
