import math
import uuid
from decimal import Decimal
from pathlib import Path
from typing import Annotated

from fastapi import APIRouter, Depends, File, Form, Query, UploadFile

from app.application.exceptions import ValidationError
from app.application.services.sales_service import SalesService
from app.domain.entities.user_registration import UserRegistration
from app.domain.enums import (
    CustomerBusinessType,
    PaymentTerms,
    PreferredPaymentMethod,
    SalesDeliveryStatus,
    SalesInvoiceStatus,
    SalesOrderStatus,
    SalesPaymentStatus,
)
from app.presentation.auth_dependencies import get_current_user
from app.presentation.company_dependencies import get_validated_company_id_query
from app.presentation.dependencies import get_sales_service
from app.presentation.schemas.common import DataTableResponse, MessageResponse
from app.presentation.schemas.sales import (
    AvailabilityCheckResponse,
    AvailabilityLineSummary,
    CustomerResponse,
    SalesDeliveryCreate,
    SalesDeliveryResponse,
    SalesInvoiceCreate,
    SalesInvoiceResponse,
    SalesOrderCreate,
    SalesOrderPickRequest,
    SalesOrderResponse,
    SalesOrderUpdate,
    SalesPaymentCreate,
    SalesPaymentResponse,
)

router = APIRouter(prefix="/sales", tags=["Sales"])

CompanyId = Annotated[str, Depends(get_validated_company_id_query)]

_STATIC_ROOT = Path(__file__).resolve().parents[4] / "static"
_LOGO_ALLOWED_EXT = {".png", ".jpg", ".jpeg"}
_LOGO_MAX_BYTES = 2 * 1024 * 1024


def _build_datatable(data: list, total: int, page: int, page_size: int) -> DataTableResponse:
    return DataTableResponse(
        data=data,
        total=total,
        page=page,
        page_size=page_size,
        total_pages=max(1, math.ceil(total / page_size)) if page_size else 1,
    )


async def _save_customer_logo(company_id: str, logo: UploadFile) -> str:
    filename = logo.filename or ""
    ext = Path(filename).suffix.lower()
    if ext not in _LOGO_ALLOWED_EXT:
        raise ValidationError("Logo must be JPG or PNG")
    content = await logo.read()
    if len(content) > _LOGO_MAX_BYTES:
        raise ValidationError("Logo must be 2MB or smaller")
    dest_dir = _STATIC_ROOT / "customers" / company_id
    dest_dir.mkdir(parents=True, exist_ok=True)
    stored_name = f"{uuid.uuid4().hex}{ext}"
    (dest_dir / stored_name).write_bytes(content)
    return f"/static/customers/{company_id}/{stored_name}"


def _to_customer_response(customer) -> CustomerResponse:
    return CustomerResponse(
        id=customer.id or "",
        company_id=customer.company_id,
        code=customer.code,
        name=customer.name,
        business_type=customer.business_type,
        contact_person=customer.contact_person,
        designation=customer.designation,
        phone=customer.phone,
        alternate_phone=customer.alternate_phone,
        email=customer.email,
        website=customer.website,
        ntn=customer.ntn,
        sales_tax_no=customer.sales_tax_no,
        address_line1=customer.address_line1,
        address_line2=customer.address_line2,
        city=customer.city,
        state_province=customer.state_province,
        country=customer.country,
        postal_code=customer.postal_code,
        notes=customer.notes,
        payment_terms=customer.payment_terms,
        payment_terms_days=customer.payment_terms_days,
        credit_limit=customer.credit_limit,
        opening_balance=customer.opening_balance,
        currency=customer.currency,
        so_prefix=customer.so_prefix,
        default_warehouse_id=customer.default_warehouse_id,
        default_warehouse_code=getattr(customer, "default_warehouse_code", None),
        default_warehouse_name=getattr(customer, "default_warehouse_name", None),
        preferred_payment_method=customer.preferred_payment_method,
        logo=customer.logo,
        address=customer.address,
        account_id=customer.account_id,
        is_active=customer.is_active,
        created_at=customer.created_at,
        updated_at=customer.updated_at,
    )


# ---- Customers ----
@router.post("/customers", response_model=CustomerResponse, status_code=201)
async def create_customer(
    company_id: CompanyId,
    name: Annotated[str, Form(min_length=1, max_length=200, description="Customer name")],
    contact_person: Annotated[str, Form(min_length=1, max_length=200)],
    phone: Annotated[str, Form(min_length=1, max_length=50)],
    address_line1: Annotated[str, Form(min_length=1, max_length=255)],
    city: Annotated[str, Form(min_length=1, max_length=100)],
    code: Annotated[
        str | None,
        Form(min_length=1, max_length=30, description="Auto CUS-00001 if left blank"),
    ] = None,
    business_type: Annotated[CustomerBusinessType | None, Form()] = None,
    designation: Annotated[str | None, Form(max_length=100)] = None,
    alternate_phone: Annotated[str | None, Form(max_length=50)] = None,
    email: Annotated[str | None, Form(max_length=255)] = None,
    website: Annotated[str | None, Form(max_length=255)] = None,
    ntn: Annotated[str | None, Form(max_length=50)] = None,
    sales_tax_no: Annotated[str | None, Form(max_length=50)] = None,
    address_line2: Annotated[str | None, Form(max_length=255)] = None,
    state_province: Annotated[str | None, Form(max_length=100)] = None,
    country: Annotated[str, Form(min_length=1, max_length=100)] = "Pakistan",
    postal_code: Annotated[str | None, Form(max_length=30)] = None,
    notes: Annotated[str | None, Form(max_length=500)] = None,
    payment_terms: Annotated[PaymentTerms | None, Form()] = None,
    payment_terms_days: Annotated[int, Form(ge=0)] = 30,
    credit_limit: Annotated[Decimal, Form(ge=0)] = Decimal("0.00"),
    opening_balance: Annotated[Decimal, Form()] = Decimal("0.00"),
    currency: Annotated[str, Form(max_length=10)] = "PKR",
    so_prefix: Annotated[str | None, Form(max_length=30)] = None,
    default_warehouse_id: Annotated[str | None, Form()] = None,
    preferred_payment_method: Annotated[PreferredPaymentMethod | None, Form()] = None,
    account_id: Annotated[str | None, Form()] = None,
    is_active: Annotated[bool, Form(description="Status Active/Inactive")] = True,
    logo: Annotated[
        UploadFile | None,
        File(description="JPG or PNG — max 2MB"),
    ] = None,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    """Create customer and auto-create AR CoA leaf under Customers (1140)."""
    payload = {
        "code": code,
        "name": name,
        "business_type": business_type.value if business_type else None,
        "contact_person": contact_person,
        "designation": designation,
        "phone": phone,
        "alternate_phone": alternate_phone,
        "email": email,
        "website": website,
        "ntn": ntn,
        "sales_tax_no": sales_tax_no,
        "address_line1": address_line1,
        "address_line2": address_line2,
        "city": city,
        "state_province": state_province,
        "country": country,
        "postal_code": postal_code,
        "notes": notes,
        "payment_terms": payment_terms.value if payment_terms else None,
        "payment_terms_days": payment_terms_days,
        "credit_limit": credit_limit,
        "opening_balance": opening_balance,
        "currency": currency,
        "so_prefix": so_prefix,
        "default_warehouse_id": default_warehouse_id,
        "preferred_payment_method": (
            preferred_payment_method.value if preferred_payment_method else None
        ),
        "account_id": account_id,
        "is_active": is_active,
    }
    if logo is not None and logo.filename:
        payload["logo"] = await _save_customer_logo(company_id, logo)
    customer = await service.create_customer(current_user, company_id, payload)
    return _to_customer_response(customer)


@router.get("/customers", response_model=DataTableResponse[CustomerResponse])
async def list_customers(
    company_id: CompanyId,
    is_active: bool | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_customers(
        current_user, company_id, is_active, skip, page_size
    )
    return _build_datatable(
        [_to_customer_response(i) for i in items], total, page, page_size
    )


@router.get("/customers/{customer_id}", response_model=CustomerResponse)
async def get_customer(
    company_id: CompanyId,
    customer_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    customer = await service.get_customer(current_user, company_id, customer_id)
    return _to_customer_response(customer)


@router.put("/customers/{customer_id}", response_model=CustomerResponse)
async def update_customer(
    company_id: CompanyId,
    customer_id: str,
    code: Annotated[str | None, Form(min_length=1, max_length=30)] = None,
    name: Annotated[str | None, Form(min_length=1, max_length=200)] = None,
    business_type: Annotated[CustomerBusinessType | None, Form()] = None,
    contact_person: Annotated[str | None, Form(min_length=1, max_length=200)] = None,
    designation: Annotated[str | None, Form(max_length=100)] = None,
    phone: Annotated[str | None, Form(min_length=1, max_length=50)] = None,
    alternate_phone: Annotated[str | None, Form(max_length=50)] = None,
    email: Annotated[str | None, Form(max_length=255)] = None,
    website: Annotated[str | None, Form(max_length=255)] = None,
    ntn: Annotated[str | None, Form(max_length=50)] = None,
    sales_tax_no: Annotated[str | None, Form(max_length=50)] = None,
    address_line1: Annotated[str | None, Form(min_length=1, max_length=255)] = None,
    address_line2: Annotated[str | None, Form(max_length=255)] = None,
    city: Annotated[str | None, Form(min_length=1, max_length=100)] = None,
    state_province: Annotated[str | None, Form(max_length=100)] = None,
    country: Annotated[str | None, Form(min_length=1, max_length=100)] = None,
    postal_code: Annotated[str | None, Form(max_length=30)] = None,
    notes: Annotated[str | None, Form(max_length=500)] = None,
    payment_terms: Annotated[PaymentTerms | None, Form()] = None,
    payment_terms_days: Annotated[int | None, Form(ge=0)] = None,
    credit_limit: Annotated[Decimal | None, Form(ge=0)] = None,
    opening_balance: Annotated[Decimal | None, Form()] = None,
    currency: Annotated[str | None, Form(max_length=10)] = None,
    so_prefix: Annotated[str | None, Form(max_length=30)] = None,
    default_warehouse_id: Annotated[str | None, Form()] = None,
    preferred_payment_method: Annotated[PreferredPaymentMethod | None, Form()] = None,
    account_id: Annotated[str | None, Form()] = None,
    is_active: Annotated[bool | None, Form()] = None,
    logo: Annotated[
        UploadFile | None,
        File(description="JPG or PNG — max 2MB"),
    ] = None,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    payload: dict = {}
    if code is not None:
        payload["code"] = code
    if name is not None:
        payload["name"] = name
    if business_type is not None:
        payload["business_type"] = business_type.value
    if contact_person is not None:
        payload["contact_person"] = contact_person
    if designation is not None:
        payload["designation"] = designation
    if phone is not None:
        payload["phone"] = phone
    if alternate_phone is not None:
        payload["alternate_phone"] = alternate_phone
    if email is not None:
        payload["email"] = email
    if website is not None:
        payload["website"] = website
    if ntn is not None:
        payload["ntn"] = ntn
    if sales_tax_no is not None:
        payload["sales_tax_no"] = sales_tax_no
    if address_line1 is not None:
        payload["address_line1"] = address_line1
    if address_line2 is not None:
        payload["address_line2"] = address_line2
    if city is not None:
        payload["city"] = city
    if state_province is not None:
        payload["state_province"] = state_province
    if country is not None:
        payload["country"] = country
    if postal_code is not None:
        payload["postal_code"] = postal_code
    if notes is not None:
        payload["notes"] = notes
    if payment_terms is not None:
        payload["payment_terms"] = payment_terms.value
    if payment_terms_days is not None:
        payload["payment_terms_days"] = payment_terms_days
    if credit_limit is not None:
        payload["credit_limit"] = credit_limit
    if opening_balance is not None:
        payload["opening_balance"] = opening_balance
    if currency is not None:
        payload["currency"] = currency
    if so_prefix is not None:
        payload["so_prefix"] = so_prefix
    if default_warehouse_id is not None:
        payload["default_warehouse_id"] = default_warehouse_id or None
    if preferred_payment_method is not None:
        payload["preferred_payment_method"] = preferred_payment_method.value
    if account_id is not None:
        payload["account_id"] = account_id or None
    if is_active is not None:
        payload["is_active"] = is_active
    if logo is not None and logo.filename:
        payload["logo"] = await _save_customer_logo(company_id, logo)
    customer = await service.update_customer(current_user, company_id, customer_id, payload)
    return _to_customer_response(customer)


@router.delete("/customers/{customer_id}", response_model=MessageResponse)
async def delete_customer(
    company_id: CompanyId,
    customer_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    await service.delete_customer(current_user, company_id, customer_id)
    return MessageResponse(message="Customer deleted successfully")


# ---- Sales orders ----
@router.post("/orders", response_model=SalesOrderResponse, status_code=201)
async def create_order(
    company_id: CompanyId,
    payload: SalesOrderCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    """Create draft sales order."""
    order = await service.create_sales_order(current_user, company_id, payload.model_dump())
    return SalesOrderResponse.model_validate(order)


@router.get("/orders", response_model=DataTableResponse[SalesOrderResponse])
async def list_orders(
    company_id: CompanyId,
    status: SalesOrderStatus | None = None,
    customer_id: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_sales_orders(
        current_user, company_id, status, customer_id, skip, page_size
    )
    return _build_datatable(
        [SalesOrderResponse.model_validate(i) for i in items], total, page, page_size
    )


@router.get("/orders/{order_id}", response_model=SalesOrderResponse)
async def get_order(
    company_id: CompanyId,
    order_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    order = await service.get_sales_order(current_user, company_id, order_id)
    return SalesOrderResponse.model_validate(order)


@router.put("/orders/{order_id}", response_model=SalesOrderResponse)
async def update_order(
    company_id: CompanyId,
    order_id: str,
    payload: SalesOrderUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    order = await service.update_sales_order(
        current_user, company_id, order_id, payload.model_dump(exclude_unset=True)
    )
    return SalesOrderResponse.model_validate(order)


@router.post("/orders/{order_id}/confirm", response_model=SalesOrderResponse)
async def confirm_order(
    company_id: CompanyId,
    order_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    """Confirm Sales Order — draft → confirmed."""
    order = await service.confirm_sales_order(current_user, company_id, order_id)
    return SalesOrderResponse.model_validate(order)


@router.post("/orders/{order_id}/check-availability", response_model=AvailabilityCheckResponse)
async def check_availability(
    company_id: CompanyId,
    order_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    """Check Availability — compare line qty vs on_hand - reserved."""
    order, summary = await service.check_availability(current_user, company_id, order_id)
    return AvailabilityCheckResponse(
        order=SalesOrderResponse.model_validate(order),
        all_available=summary["all_available"],
        lines=[AvailabilityLineSummary.model_validate(line) for line in summary["lines"]],
    )


@router.post("/orders/{order_id}/reserve", response_model=SalesOrderResponse)
async def reserve_stock(
    company_id: CompanyId,
    order_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    """Reserve Stock — increase inventory quantity_reserved."""
    order = await service.reserve_stock(current_user, company_id, order_id)
    return SalesOrderResponse.model_validate(order)


@router.post("/orders/{order_id}/pick", response_model=SalesOrderResponse)
async def pick_items(
    company_id: CompanyId,
    order_id: str,
    payload: SalesOrderPickRequest | None = None,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    """Pick Items — status picked; store picked qty on lines."""
    data = payload.model_dump() if payload else None
    order = await service.pick_items(current_user, company_id, order_id, data)
    return SalesOrderResponse.model_validate(order)


@router.post("/orders/{order_id}/cancel", response_model=SalesOrderResponse)
async def cancel_order(
    company_id: CompanyId,
    order_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    order = await service.cancel_sales_order(current_user, company_id, order_id)
    return SalesOrderResponse.model_validate(order)


@router.post("/orders/{order_id}/close", response_model=SalesOrderResponse)
async def close_order(
    company_id: CompanyId,
    order_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    order = await service.close_sales_order(current_user, company_id, order_id)
    return SalesOrderResponse.model_validate(order)


# ---- Deliveries (Stock Out Flow) ----
@router.post("/deliveries", response_model=SalesDeliveryResponse, status_code=201)
async def create_delivery(
    company_id: CompanyId,
    payload: SalesDeliveryCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    """A. Delivery / GDN - create draft goods delivery note (stock out) against a sales order."""
    delivery = await service.create_delivery(current_user, company_id, payload.model_dump())
    return SalesDeliveryResponse.model_validate(delivery)


@router.get("/deliveries", response_model=DataTableResponse[SalesDeliveryResponse])
async def list_deliveries(
    company_id: CompanyId,
    status: SalesDeliveryStatus | None = None,
    sales_order_id: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    """List delivery / stock-out notes."""
    skip = (page - 1) * page_size
    items, total = await service.list_deliveries(
        current_user, company_id, status, sales_order_id, skip, page_size
    )
    return _build_datatable(
        [SalesDeliveryResponse.model_validate(i) for i in items], total, page, page_size
    )


@router.get("/deliveries/{delivery_id}", response_model=SalesDeliveryResponse)
async def get_delivery(
    company_id: CompanyId,
    delivery_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    delivery = await service.get_delivery(current_user, company_id, delivery_id)
    return SalesDeliveryResponse.model_validate(delivery)


@router.post("/deliveries/{delivery_id}/confirm", response_model=SalesDeliveryResponse)
async def confirm_delivery(
    company_id: CompanyId,
    delivery_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    """Confirm Delivery / Stock Out.

    Runs stock deduction, inventory update, transaction history, and COGS report voucher.
    """
    delivery = await service.confirm_delivery(current_user, company_id, delivery_id)
    return SalesDeliveryResponse.model_validate(delivery)


# ---- Invoices ----
@router.post("/invoices", response_model=SalesInvoiceResponse, status_code=201)
async def create_invoice(
    company_id: CompanyId,
    payload: SalesInvoiceCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    invoice = await service.create_invoice(current_user, company_id, payload.model_dump())
    return SalesInvoiceResponse.model_validate(invoice)


@router.get("/invoices", response_model=DataTableResponse[SalesInvoiceResponse])
async def list_invoices(
    company_id: CompanyId,
    status: SalesInvoiceStatus | None = None,
    customer_id: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_invoices(
        current_user, company_id, status, customer_id, skip, page_size
    )
    return _build_datatable(
        [SalesInvoiceResponse.model_validate(i) for i in items], total, page, page_size
    )


@router.get("/invoices/{invoice_id}", response_model=SalesInvoiceResponse)
async def get_invoice(
    company_id: CompanyId,
    invoice_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    invoice = await service.get_invoice(current_user, company_id, invoice_id)
    return SalesInvoiceResponse.model_validate(invoice)


@router.post("/invoices/{invoice_id}/post", response_model=SalesInvoiceResponse)
async def post_invoice(
    company_id: CompanyId,
    invoice_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    """Post AR voucher — debit customer AR, credit revenue/sales."""
    invoice = await service.post_invoice(current_user, company_id, invoice_id)
    return SalesInvoiceResponse.model_validate(invoice)


# ---- Payments ----
@router.post("/payments", response_model=SalesPaymentResponse, status_code=201)
async def create_payment(
    company_id: CompanyId,
    payload: SalesPaymentCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    payment = await service.create_payment(current_user, company_id, payload.model_dump())
    return SalesPaymentResponse.model_validate(payment)


@router.get("/payments", response_model=DataTableResponse[SalesPaymentResponse])
async def list_payments(
    company_id: CompanyId,
    status: SalesPaymentStatus | None = None,
    customer_id: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_payments(
        current_user, company_id, status, customer_id, skip, page_size
    )
    return _build_datatable(
        [SalesPaymentResponse.model_validate(i) for i in items], total, page, page_size
    )


@router.get("/payments/{payment_id}", response_model=SalesPaymentResponse)
async def get_payment(
    company_id: CompanyId,
    payment_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    payment = await service.get_payment(current_user, company_id, payment_id)
    return SalesPaymentResponse.model_validate(payment)


@router.post("/payments/{payment_id}/post", response_model=SalesPaymentResponse)
async def post_payment(
    company_id: CompanyId,
    payment_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: SalesService = Depends(get_sales_service),
):
    """Post receipt voucher — debit bank/cash, credit AR."""
    payment = await service.post_payment(current_user, company_id, payment_id)
    return SalesPaymentResponse.model_validate(payment)
