"""API key management routes for integrations."""
from __future__ import annotations

import uuid
from datetime import datetime
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, Query, status
from pydantic import BaseModel, Field
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from normbase.db.connection import get_db
from normbase.models.norm import APIKey
from security.auth.api_keys import (
    generate_api_key,
    hash_api_key,
    mask_api_key,
    verify_api_key_hash,
)

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


class APIKeyIssueRequest(BaseModel):
    agent_id: str = Field(..., min_length=1)
    name: Optional[str] = None
    expires_at: Optional[datetime] = None
    scopes: List[str] = Field(default_factory=lambda: ["*"])


class APIKeyIssueResponse(BaseModel):
    key_id: str
    agent_id: str
    name: Optional[str] = None
    api_key: str
    key_preview: str
    scopes: List[str]
    is_active: bool
    expires_at: Optional[datetime] = None
    created_at: datetime


class APIKeyListItem(BaseModel):
    key_id: str
    agent_id: Optional[str] = None
    name: Optional[str] = None
    key_preview: str
    scopes: List[str]
    is_active: bool
    expires_at: Optional[datetime] = None
    created_at: datetime


class APIKeyResolveRequest(BaseModel):
    api_key: str = Field(..., min_length=8)


class APIKeyResolveResponse(BaseModel):
    key_id: str
    agent_id: str
    scopes: List[str]
    is_active: bool
    expires_at: Optional[datetime] = None


def _ensure_not_expired(record: APIKey) -> bool:
    if record.expires_at is None:
        return True
    return record.expires_at > datetime.utcnow()


@router.post("/issue", response_model=APIKeyIssueResponse, status_code=status.HTTP_201_CREATED)
async def issue_api_key(
    request: APIKeyIssueRequest,
    db: AsyncSession = Depends(get_db),
):
    plain = generate_api_key()
    record = APIKey(
        key_hash=hash_api_key(plain),
        name=request.name or f"agent:{request.agent_id}",
        agent_id=request.agent_id,
        is_active=True,
        expires_at=request.expires_at,
    )
    db.add(record)
    await db.commit()
    await db.refresh(record)
    return APIKeyIssueResponse(
        key_id=str(record.id),
        agent_id=record.agent_id or request.agent_id,
        name=record.name,
        api_key=plain,
        key_preview=mask_api_key(plain),
        scopes=request.scopes or ["*"],
        is_active=record.is_active,
        expires_at=record.expires_at,
        created_at=record.created_at,
    )


@router.get("", response_model=List[APIKeyListItem])
async def list_api_keys(
    agent_id: Optional[str] = Query(None, description="Filter by agent_id"),
    include_inactive: bool = Query(False),
    db: AsyncSession = Depends(get_db),
):
    query = select(APIKey).order_by(APIKey.created_at.desc())
    if agent_id:
        query = query.where(APIKey.agent_id == agent_id)
    if not include_inactive:
        query = query.where(APIKey.is_active.is_(True))
    rows = (await db.execute(query)).scalars().all()
    return [
        APIKeyListItem(
            key_id=str(item.id),
            agent_id=item.agent_id,
            name=item.name,
            key_preview="***",
            scopes=["*"],
            is_active=item.is_active,
            expires_at=item.expires_at,
            created_at=item.created_at,
        )
        for item in rows
    ]


@router.post("/{key_id}/revoke", response_model=APIKeyListItem)
async def revoke_api_key(
    key_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
):
    row = (await db.execute(select(APIKey).where(APIKey.id == key_id))).scalar_one_or_none()
    if not row:
        raise HTTPException(status_code=404, detail="API key not found")
    row.is_active = False
    await db.commit()
    await db.refresh(row)
    return APIKeyListItem(
        key_id=str(row.id),
        agent_id=row.agent_id,
        name=row.name,
        key_preview="***",
        scopes=["*"],
        is_active=row.is_active,
        expires_at=row.expires_at,
        created_at=row.created_at,
    )


@router.post("/{key_id}/rotate", response_model=APIKeyIssueResponse)
async def rotate_api_key(
    key_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
):
    row = (await db.execute(select(APIKey).where(APIKey.id == key_id))).scalar_one_or_none()
    if not row:
        raise HTTPException(status_code=404, detail="API key not found")
    plain = generate_api_key()
    row.key_hash = hash_api_key(plain)
    row.is_active = True
    await db.commit()
    await db.refresh(row)
    return APIKeyIssueResponse(
        key_id=str(row.id),
        agent_id=row.agent_id or "",
        name=row.name,
        api_key=plain,
        key_preview=mask_api_key(plain),
        scopes=["*"],
        is_active=row.is_active,
        expires_at=row.expires_at,
        created_at=row.created_at,
    )


@router.post("/resolve", response_model=APIKeyResolveResponse)
async def resolve_api_key(
    request: APIKeyResolveRequest,
    db: AsyncSession = Depends(get_db),
):
    hashed = hash_api_key(request.api_key)
    row = (
        await db.execute(select(APIKey).where(APIKey.key_hash == hashed))
    ).scalar_one_or_none()
    if not row:
        raise HTTPException(status_code=401, detail="Invalid API key")
    if not row.is_active or not _ensure_not_expired(row):
        raise HTTPException(status_code=401, detail="API key inactive or expired")
    if not verify_api_key_hash(request.api_key, row.key_hash):
        raise HTTPException(status_code=401, detail="Invalid API key")
    return APIKeyResolveResponse(
        key_id=str(row.id),
        agent_id=row.agent_id or "",
        scopes=["*"],
        is_active=row.is_active,
        expires_at=row.expires_at,
    )

