from typing import List

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from .. import models, schemas
from ..db import get_db
from ..cache import CACHE_TTL_SECONDS, get_cache, safe_cache_delete, safe_cache_set
from ..security import require_admin

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


def get_domain_by_name(db: Session, name: str) -> models.Domain:
    domain = db.query(models.Domain).filter(models.Domain.name == name).first()
    if not domain:
        raise HTTPException(status_code=404, detail="Domain not found")
    if not domain.is_active:
        raise HTTPException(status_code=400, detail="Domain is inactive")
    return domain


def cache_alias(cache, source: str, alias: models.Alias) -> None:
    safe_cache_set(cache, f"alias:{source}", alias.destination, ex=CACHE_TTL_SECONDS)


@router.get("/", response_model=List[schemas.AliasOut])
def list_aliases(db: Session = Depends(get_db), _: str = Depends(require_admin)):
    return db.query(models.Alias).all()


@router.post("/", response_model=schemas.AliasOut)
def create_alias(payload: schemas.AliasCreate, db: Session = Depends(get_db), cache=Depends(get_cache), _: str = Depends(require_admin)):
    domain = get_domain_by_name(db, payload.domain)
    source_email = f"{payload.source_local}@{domain.name}"
    exists = db.query(models.Alias).filter(models.Alias.domain_id == domain.id, models.Alias.source_local == payload.source_local, models.Alias.destination == payload.destination).first()
    if exists:
        raise HTTPException(status_code=400, detail="Alias already exists")
    alias = models.Alias(
        domain_id=domain.id,
        source_local=payload.source_local,
        destination=payload.destination,
        is_active=payload.is_active,
    )
    db.add(alias)
    try:
        db.commit()
    except IntegrityError as exc:
        db.rollback()
        raise HTTPException(status_code=409, detail="Alias already exists") from exc
    db.refresh(alias)
    cache_alias(cache, source_email, alias)
    return alias


@router.patch("/{alias_id}", response_model=schemas.AliasOut)
def update_alias(alias_id: int, payload: schemas.AliasUpdate, db: Session = Depends(get_db), cache=Depends(get_cache), _: str = Depends(require_admin)):
    alias = db.query(models.Alias).filter(models.Alias.id == alias_id).first()
    if not alias:
        raise HTTPException(status_code=404, detail="Alias not found")
    if payload.is_active is not None:
        alias.is_active = payload.is_active
    db.commit()
    db.refresh(alias)
    source_email = f"{alias.source_local}@{alias.domain.name}" if alias.domain else alias.source_local
    cache_alias(cache, source_email, alias)
    return alias


@router.delete("/{alias_id}", response_model=dict)
def delete_alias(alias_id: int, db: Session = Depends(get_db), cache=Depends(get_cache), _: str = Depends(require_admin)):
    alias = db.query(models.Alias).filter(models.Alias.id == alias_id).first()
    if not alias:
        raise HTTPException(status_code=404, detail="Alias not found")
    source_email = f"{alias.source_local}@{alias.domain.name}" if alias.domain else alias.source_local
    db.delete(alias)
    db.commit()
    safe_cache_delete(cache, f"alias:{source_email}")
    return {"deleted": True, "source": source_email}
