from uuid import UUID

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

from app.api.workspace import get_verified_current_user
from app.core.permissions import Permission
from app.db.session import get_db
from app.models.day_close import DayClose
from app.models.user import User
from app.schemas.day_close import DayCloseCreate, DayClosePeriod, DayClosePreview, DayCloseResponse
from app.services.day_close import expected_cash, overlaps_existing_close
from app.services.audit import record_audit
from app.services.rbac import require_branch_access, require_permission

router = APIRouter(tags=["Day Close"])


def _member_with_branch(db: Session, user: User, business_id: UUID, branch_id: UUID):
    member = require_permission(db, user, business_id, Permission.MANAGE_FINANCES)
    require_branch_access(db, member, business_id, branch_id)
    return member


@router.post("/api/businesses/{business_id}/day-closes/preview", response_model=DayClosePreview)
def preview(business_id: UUID, data: DayClosePeriod, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    _member_with_branch(db, user, business_id, data.branch_id)
    return DayClosePreview(**data.model_dump(), expected_cash=expected_cash(db, business_id, data.branch_id, data.period_start, data.period_end))


@router.post("/api/businesses/{business_id}/day-closes", response_model=DayCloseResponse, status_code=status.HTTP_201_CREATED)
def create(business_id: UUID, data: DayCloseCreate, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    _member_with_branch(db, user, business_id, data.branch_id)
    if overlaps_existing_close(db, business_id, data.branch_id, data.period_start, data.period_end):
        raise HTTPException(status.HTTP_409_CONFLICT, "This branch already has a day close overlapping the selected period.")
    expected = expected_cash(db, business_id, data.branch_id, data.period_start, data.period_end)
    close = DayClose(business_id=business_id, branch_id=data.branch_id, period_start=data.period_start, period_end=data.period_end, expected_cash=expected, counted_cash=data.counted_cash, difference=data.counted_cash - expected, notes=data.notes, closed_by=user.id)
    db.add(close)
    db.flush()
    record_audit(db, business_id, "DAY_CLOSE_CREATED", "day_close", close.id, user.id, data.branch_id, {"period_start": data.period_start.isoformat(), "period_end": data.period_end.isoformat(), "expected_cash": str(expected), "counted_cash": str(data.counted_cash), "difference": str(close.difference)})
    try:
        db.commit()
    except IntegrityError:
        db.rollback()
        raise HTTPException(status.HTTP_409_CONFLICT, "This branch already has a day close for the selected period.")
    db.refresh(close)
    return close


@router.get("/api/businesses/{business_id}/day-closes", response_model=list[DayCloseResponse])
def listing(business_id: UUID, branch_id: UUID | None = None, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    member = require_permission(db, user, business_id, Permission.MANAGE_FINANCES)
    query = select(DayClose).where(DayClose.business_id == business_id)
    if branch_id:
        require_branch_access(db, member, business_id, branch_id)
        query = query.where(DayClose.branch_id == branch_id)
    elif not member.all_branches:
        from app.models.membership import MembershipBranch
        query = query.join(MembershipBranch, MembershipBranch.branch_id == DayClose.branch_id).where(MembershipBranch.membership_id == member.id)
    return list(db.scalars(query.order_by(DayClose.closed_at.desc())))


@router.get("/api/businesses/{business_id}/day-closes/{close_id}", response_model=DayCloseResponse)
def detail(business_id: UUID, close_id: UUID, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    close = db.scalar(select(DayClose).where(DayClose.id == close_id, DayClose.business_id == business_id))
    if close is None:
        raise HTTPException(status.HTTP_404_NOT_FOUND, "Day close not found.")
    _member_with_branch(db, user, business_id, close.branch_id)
    return close
