from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func

from app.database import get_db
from app.models.vendor import Vendor
from app.models.user import User
from app.schemas.vendor import VendorCreate, VendorUpdate, VendorOut
from app.deps import get_current_user, require_role
from app.utils import auto_code

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


@router.get("", response_model=list[VendorOut])
async def list_vendors(
    search: Optional[str] = Query(None),
    db: AsyncSession = Depends(get_db),
    _: User = Depends(get_current_user),
):
    q = select(Vendor).order_by(Vendor.name)
    if search:
        q = q.where(Vendor.name.ilike(f"%{search}%"))
    result = await db.execute(q)
    return result.scalars().all()


@router.post("", response_model=VendorOut, status_code=201)
async def create_vendor(
    body: VendorCreate,
    db: AsyncSession = Depends(get_db),
    _: User = Depends(get_current_user),
):
    code = (body.code or auto_code(body.name)).upper()

    existing = await db.execute(select(Vendor).where(Vendor.code == code))
    if existing.scalar_one_or_none():
        cnt = await db.scalar(select(func.count()).select_from(Vendor))
        code = code + str((cnt or 0) + 1)

    vendor = Vendor(name=body.name, code=code)
    db.add(vendor)
    try:
        await db.commit()
        await db.refresh(vendor)
    except Exception:
        await db.rollback()
        raise
    return vendor


@router.put("/{vendor_id}", response_model=VendorOut)
async def update_vendor(
    vendor_id: int,
    body: VendorUpdate,
    db: AsyncSession = Depends(get_db),
    _: User = Depends(require_role("admin")),
):
    result = await db.execute(select(Vendor).where(Vendor.id == vendor_id))
    vendor = result.scalar_one_or_none()
    if not vendor:
        raise HTTPException(status_code=404, detail="Vendor not found")

    if body.name is not None:
        vendor.name = body.name
    if body.code is not None:
        vendor.code = body.code.upper()

    try:
        await db.commit()
        await db.refresh(vendor)
    except Exception:
        await db.rollback()
        raise
    return vendor


@router.delete("/{vendor_id}", status_code=204)
async def delete_vendor(
    vendor_id: int,
    db: AsyncSession = Depends(get_db),
    _: User = Depends(require_role("admin")),
):
    result = await db.execute(select(Vendor).where(Vendor.id == vendor_id))
    vendor = result.scalar_one_or_none()
    if not vendor:
        raise HTTPException(status_code=404, detail="Vendor not found")

    try:
        await db.delete(vendor)
        await db.commit()
    except Exception:
        await db.rollback()
        raise
