"""Sales router — opportunities and contracts."""
from decimal import Decimal
from uuid import UUID
from typing import Optional

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

from src.core.dependencies import get_current_user, require_min_role, require_tenant
from src.core.constants import UserRole
from src.core.schemas.response import success, paginated
from src.core.exceptions import NotFoundError
from src.database.session import get_db
from src.apps.auth.models.user import User
from src.apps.sales.models.opportunity import Opportunity
from src.apps.sales.models.contract import Contract
from src.apps.sales.models.contract_line_item import ContractLineItem
from src.apps.sales.schemas.requests import (
    OpportunityCreateRequest,
    OpportunityUpdateRequest,
    ContractCreateRequest,
    ContractUpdateRequest,
)
from src.apps.sales.schemas.responses import OpportunityResponse, ContractResponse

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


async def _get_opportunity(opp_id: UUID, tenant_id: str, db: AsyncSession) -> Opportunity:
    result = await db.execute(
        select(Opportunity).where(
            Opportunity.id == opp_id, Opportunity.tenant_id == tenant_id
        )
    )
    opp = result.scalar_one_or_none()
    if not opp:
        raise NotFoundError("Opportunity not found")
    return opp


async def _get_contract(contract_id: UUID, tenant_id: str, db: AsyncSession) -> Contract:
    result = await db.execute(
        select(Contract).where(
            Contract.id == contract_id, Contract.tenant_id == tenant_id
        )
    )
    contract = result.scalar_one_or_none()
    if not contract:
        raise NotFoundError("Contract not found")
    return contract


# --- Opportunities ---

@router.get("/opportunities")
async def list_opportunities(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    stage: Optional[str] = Query(None),
    current_user: User = Depends(require_min_role(UserRole.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    filters = [Opportunity.tenant_id == tenant_id]
    if stage:
        filters.append(Opportunity.stage == stage)

    total = (await db.execute(select(func.count()).select_from(Opportunity).where(*filters))).scalar_one()
    offset = (page - 1) * page_size
    result = await db.execute(
        select(Opportunity).where(*filters)
        .order_by(Opportunity.created_at.desc())
        .offset(offset).limit(page_size)
    )
    items = [OpportunityResponse.model_validate(o) for o in result.scalars().all()]
    return paginated(items, total, page, page_size)


@router.get("/opportunities/{opportunity_id}")
async def get_opportunity(
    opportunity_id: UUID,
    current_user: User = Depends(require_min_role(UserRole.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    opp = await _get_opportunity(opportunity_id, tenant_id, db)
    return success(OpportunityResponse.model_validate(opp))


@router.post("/opportunities", status_code=201)
async def create_opportunity(
    body: OpportunityCreateRequest,
    current_user: User = Depends(require_min_role(UserRole.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    opp = Opportunity(
        tenant_id=tenant_id,
        stage="inquiry",
        **body.model_dump(),
    )
    db.add(opp)
    await db.flush()
    await db.refresh(opp)
    return success(OpportunityResponse.model_validate(opp), "Opportunity created")


@router.patch("/opportunities/{opportunity_id}")
async def update_opportunity(
    opportunity_id: UUID,
    body: OpportunityUpdateRequest,
    current_user: User = Depends(require_min_role(UserRole.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    opp = await _get_opportunity(opportunity_id, tenant_id, db)
    for field, value in body.model_dump(exclude_unset=True).items():
        setattr(opp, field, value)
    await db.flush()
    await db.refresh(opp)
    return success(OpportunityResponse.model_validate(opp))


# --- Contracts ---

@router.get("/contracts")
async def list_contracts(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    current_user: User = Depends(require_min_role(UserRole.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    total = (
        await db.execute(select(func.count()).select_from(Contract).where(Contract.tenant_id == tenant_id))
    ).scalar_one()
    offset = (page - 1) * page_size
    result = await db.execute(
        select(Contract).where(Contract.tenant_id == tenant_id)
        .order_by(Contract.created_at.desc())
        .offset(offset).limit(page_size)
    )
    items = [ContractResponse.model_validate(c) for c in result.scalars().all()]
    return paginated(items, total, page, page_size)


@router.get("/contracts/{contract_id}")
async def get_contract(
    contract_id: UUID,
    current_user: User = Depends(require_min_role(UserRole.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    contract = await _get_contract(contract_id, tenant_id, db)
    return success(ContractResponse.model_validate(contract))


@router.post("/contracts", status_code=201)
async def create_contract(
    body: ContractCreateRequest,
    current_user: User = Depends(require_min_role(UserRole.MANAGER)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    count = (
        await db.execute(select(func.count()).select_from(Contract).where(Contract.tenant_id == tenant_id))
    ).scalar_one()
    contract_number = f"CON-{count + 1:05d}"

    total_amount = sum(
        (item.quantity * item.unit_price) - item.discount
        for item in body.line_items
    ) if body.line_items else Decimal("0")

    line_items_data = body.line_items
    contract_data = body.model_dump(exclude={"line_items"})

    contract = Contract(
        tenant_id=tenant_id,
        contract_number=contract_number,
        status="draft",
        total_amount=total_amount,
        **contract_data,
    )
    db.add(contract)
    await db.flush()

    for item in line_items_data:
        line_total = (item.quantity * item.unit_price) - item.discount
        li = ContractLineItem(
            contract_id=contract.id,
            description=item.description,
            quantity=item.quantity,
            unit_price=item.unit_price,
            discount=item.discount,
            line_total=line_total,
        )
        db.add(li)

    await db.flush()
    await db.refresh(contract)
    return success(ContractResponse.model_validate(contract), "Contract created")


@router.patch("/contracts/{contract_id}")
async def update_contract(
    contract_id: UUID,
    body: ContractUpdateRequest,
    current_user: User = Depends(require_min_role(UserRole.MANAGER)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    contract = await _get_contract(contract_id, tenant_id, db)
    for field, value in body.model_dump(exclude_unset=True).items():
        setattr(contract, field, value)
    await db.flush()
    await db.refresh(contract)
    return success(ContractResponse.model_validate(contract))
