from datetime import datetime
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, ConfigDict
from sqlalchemy.orm import Session

from app.core.database import get_db
from app.core.models import ExpenseCategory
from app.core.security import require_permission

router = APIRouter(prefix="/api/expense-categories", tags=["expense-categories"])


class ExpenseCategoryCreate(BaseModel):
    name: str


class ExpenseCategoryUpdate(BaseModel):
    name: str | None = None
    status: str | None = None


class ExpenseCategoryResponse(BaseModel):
    id: int
    name: str
    status: str
    created_at: datetime | None = None

    model_config = ConfigDict(from_attributes=True)


def _get_category(db: Session, category_id: int, tenant_id: int) -> ExpenseCategory:
    category = db.query(ExpenseCategory).filter(
        ExpenseCategory.id == category_id,
        ExpenseCategory.tenant_id == tenant_id,
    ).first()
    if not category:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Expense category not found")
    return category


def _normalized_name(name: str) -> str:
    value = str(name or "").strip()
    if not value:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Category name is required")
    return value


@router.get("", response_model=list[ExpenseCategoryResponse])
def list_expense_categories(
    current_user: Annotated[dict, Depends(require_permission("expenses.read"))],
    db: Session = Depends(get_db),
):
    return db.query(ExpenseCategory).filter(
        ExpenseCategory.tenant_id == current_user.tenant_id,
    ).order_by(ExpenseCategory.name).all()


@router.post("", response_model=ExpenseCategoryResponse, status_code=status.HTTP_201_CREATED)
def create_expense_category(
    data: ExpenseCategoryCreate,
    current_user: Annotated[dict, Depends(require_permission("expenses.write"))],
    db: Session = Depends(get_db),
):
    name = _normalized_name(data.name)
    exists = db.query(ExpenseCategory).filter(
        ExpenseCategory.tenant_id == current_user.tenant_id,
        ExpenseCategory.name.ilike(name),
    ).first()
    if exists:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Category already exists")
    category = ExpenseCategory(tenant_id=current_user.tenant_id, name=name)
    db.add(category)
    db.commit()
    db.refresh(category)
    return category


@router.put("/{category_id}", response_model=ExpenseCategoryResponse)
def update_expense_category(
    category_id: int,
    data: ExpenseCategoryUpdate,
    current_user: Annotated[dict, Depends(require_permission("expenses.write"))],
    db: Session = Depends(get_db),
):
    category = _get_category(db, category_id, current_user.tenant_id)
    if data.name is not None:
        category.name = _normalized_name(data.name)
    if data.status is not None:
        if data.status not in {"active", "inactive"}:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid category status")
        category.status = data.status
    db.commit()
    db.refresh(category)
    return category
