from datetime import date
from typing import Annotated, Literal

from fastapi import APIRouter, Depends, HTTPException, Path, Query, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import get_settings
from app.customers.customer_service import (
    CustomerAlreadyExistsError,
    CustomerInUseError,
    CustomerNotFoundError,
    CustomerService,
    CustomerTableNotTransactionalError,
    CustomerWriteDisabledError,
)
from app.customers.repository import CustomerRepository
from app.customers.schemas import (
    CustomerCodePreview,
    CustomerDeleteResponse,
    CustomerDetail,
    CustomerListResponse,
    CustomerStatus,
    CustomerWriteRequest,
    CustomerWriteResponse,
    MasterLookup,
    ReceivableNotificationResponse,
    ReceivableSummary,
    ReceivableTextRequest,
    ReceivableTextResponse,
    WhatsAppInfo,
    WhatsAppLinkRequest,
    WhatsAppLinkResponse,
)
from app.customers.service import build_receivable_text, build_whatsapp_url
from app.db import get_session
from app.security import AuditUserCode, AuditUserName, CurrentPrincipal


router = APIRouter(prefix="/api/pelanggan", tags=["pelanggan"])


def get_customer_service(
    session: Annotated[AsyncSession, Depends(get_session)],
) -> CustomerService:
    return CustomerService(
        CustomerRepository(session),
        write_enabled=get_settings().enable_customer_write_api,
    )


ServiceDependency = Annotated[CustomerService, Depends(get_customer_service)]


def customer_http_exception(exc: Exception) -> HTTPException:
    if isinstance(exc, CustomerWriteDisabledError):
        return HTTPException(503, "Write API belum diaktifkan")
    if isinstance(exc, CustomerTableNotTransactionalError):
        return HTTPException(503, "Tabel mspelanggan harus menggunakan InnoDB")
    if isinstance(exc, CustomerNotFoundError):
        return HTTPException(404, "Pelanggan tidak ditemukan")
    if isinstance(exc, CustomerAlreadyExistsError):
        return HTTPException(409, "Kode pelanggan sudah digunakan")
    if isinstance(exc, CustomerInUseError):
        return HTTPException(409, "Pelanggan sudah dipakai pada transaksi penjualan")
    return HTTPException(status.HTTP_422_UNPROCESSABLE_CONTENT, str(exc))


@router.get("", response_model=CustomerListResponse)
async def list_customers(
    service: ServiceDependency,
    principal: CurrentPrincipal,
    aktif: CustomerStatus | None = None,
    search: str | None = None,
    kodesales: str | None = None,
    kode_sales: str | None = None,
    include_piutang_summary: bool = True,
    page: Annotated[int, Query(ge=1)] = 1,
    page_size: Annotated[int, Query(ge=1, le=100)] = 25,
) -> CustomerListResponse:
    requested_sales = kodesales or kode_sales
    effective_sales = (
        (principal.kode_sales or principal.user_code)
        if principal.role == "SALES"
        else requested_sales
    )
    return await service.list_customers(
        aktif=aktif, search=search,
        kode_sales=effective_sales,
        include_piutang_summary=include_piutang_summary,
        page=page, page_size=page_size,
    )


@router.post("", response_model=CustomerWriteResponse, status_code=201)
async def create_customer(
    request: CustomerWriteRequest,
    service: ServiceDependency,
    principal: CurrentPrincipal,
    user_code: AuditUserCode,
    user_name: AuditUserName,
) -> CustomerWriteResponse:
    if principal.role == "SALES":
        request.sales_created = principal.kode_sales or principal.user_code
    try:
        return await service.create(request, user_code, user_name)
    except Exception as exc:
        raise customer_http_exception(exc) from exc


@router.get("/number/preview", response_model=CustomerCodePreview)
async def preview_customer_code(
    service: ServiceDependency,
    nama: Annotated[str, Query(min_length=1)],
) -> CustomerCodePreview:
    return await service.preview_code(nama)


@router.get("/master/{kind}", response_model=list[MasterLookup])
async def list_customer_master(
    service: ServiceDependency,
    kind: Literal["jenis-outlet", "sales", "jalur", "tingkat-harga", "call-code"],
    q: str | None = None,
) -> list[MasterLookup]:
    return await service.list_lookup(kind, q)


@router.get("/notif-piutang", response_model=ReceivableNotificationResponse)
async def list_receivable_notifications(
    service: ServiceDependency,
    principal: CurrentPrincipal,
    sampai_tanggal: date = date.today(),
    kodepelanggan: str | None = None,
    kodesales: str | None = None,
    kode_sales: str | None = None,
    page: Annotated[int, Query(ge=1)] = 1,
    page_size: Annotated[int, Query(ge=1, le=100)] = 25,
) -> ReceivableNotificationResponse:
    requested_sales = kodesales or kode_sales
    effective_sales = (
        (principal.kode_sales or principal.user_code)
        if principal.role == "SALES"
        else requested_sales
    )
    return await service.list_notifications(
        sampai_tanggal=sampai_tanggal, kode_pelanggan=kodepelanggan,
        kode_sales=effective_sales, page=page, page_size=page_size,
    )


@router.post("/notif-piutang/preview-text", response_model=ReceivableTextResponse)
async def preview_receivable_text(
    request: ReceivableTextRequest,
    service: ServiceDependency,
    principal: CurrentPrincipal,
) -> ReceivableTextResponse:
    effective_sales = (
        (principal.kode_sales or principal.user_code)
        if principal.role == "SALES"
        else None
    )
    if effective_sales:
        existing = await service.get_customer(
            request.kode_pelanggan, kode_sales=effective_sales
        )
        if existing is None:
            raise HTTPException(404, "Pelanggan tidak ditemukan")
    try:
        phone = await service.whatsapp_phone(request.kode_pelanggan)
        return build_receivable_text(request, phone)
    except CustomerNotFoundError as exc:
        raise customer_http_exception(exc) from exc


@router.post("/notif-piutang/whatsapp-link", response_model=WhatsAppLinkResponse)
async def create_whatsapp_link(request: WhatsAppLinkRequest) -> WhatsAppLinkResponse:
    return WhatsAppLinkResponse(url=build_whatsapp_url(request.phone, request.message_text))


@router.get("/{kode_pelanggan}/piutang-summary", response_model=ReceivableSummary)
async def get_receivable_summary(
    service: ServiceDependency,
    principal: CurrentPrincipal,
    kode_pelanggan: str,
) -> ReceivableSummary:
    effective_sales = (
        (principal.kode_sales or principal.user_code)
        if principal.role == "SALES"
        else None
    )
    try:
        return await service.receivable_summary(
            kode_pelanggan, kode_sales=effective_sales
        )
    except CustomerNotFoundError as exc:
        raise customer_http_exception(exc) from exc


@router.get("/{kode_pelanggan}/whatsapp", response_model=WhatsAppInfo)
async def get_whatsapp(
    service: ServiceDependency,
    principal: CurrentPrincipal,
    kode_pelanggan: str,
) -> WhatsAppInfo:
    effective_sales = (
        (principal.kode_sales or principal.user_code)
        if principal.role == "SALES"
        else None
    )
    if effective_sales:
        existing = await service.get_customer(
            kode_pelanggan, kode_sales=effective_sales
        )
        if existing is None:
            raise HTTPException(404, "Pelanggan tidak ditemukan")
    try:
        return WhatsAppInfo(
            kode_pelanggan=kode_pelanggan,
            phone=await service.whatsapp_phone(kode_pelanggan),
        )
    except CustomerNotFoundError as exc:
        raise customer_http_exception(exc) from exc


@router.get("/{kode_pelanggan}", response_model=CustomerDetail)
async def get_customer(
    service: ServiceDependency,
    principal: CurrentPrincipal,
    kode_pelanggan: Annotated[str, Path(min_length=1, max_length=20)],
) -> CustomerDetail:
    effective_sales = (
        (principal.kode_sales or principal.user_code)
        if principal.role == "SALES"
        else None
    )
    result = await service.get_customer(
        kode_pelanggan, kode_sales=effective_sales
    )
    if result is None:
        raise HTTPException(404, "Pelanggan tidak ditemukan")
    return result


@router.put("/{kode_pelanggan}", response_model=CustomerWriteResponse)
async def update_customer(
    request: CustomerWriteRequest,
    service: ServiceDependency,
    principal: CurrentPrincipal,
    user_code: AuditUserCode,
    user_name: AuditUserName,
    kode_pelanggan: str,
) -> CustomerWriteResponse:
    if principal.role == "SALES":
        effective_sales = principal.kode_sales or principal.user_code
        existing = await service.get_customer(
            kode_pelanggan, kode_sales=effective_sales
        )
        if existing is None:
            raise HTTPException(404, "Pelanggan tidak ditemukan")
        request.sales_created = effective_sales
    try:
        return await service.update(kode_pelanggan, request, user_code, user_name)
    except Exception as exc:
        raise customer_http_exception(exc) from exc


@router.delete("/{kode_pelanggan}", response_model=CustomerDeleteResponse)
async def delete_customer(
    service: ServiceDependency,
    principal: CurrentPrincipal,
    user_code: AuditUserCode,
    user_name: AuditUserName,
    kode_pelanggan: str,
) -> CustomerDeleteResponse:
    if principal.role == "SALES":
        effective_sales = principal.kode_sales or principal.user_code
        existing = await service.get_customer(
            kode_pelanggan, kode_sales=effective_sales
        )
        if existing is None:
            raise HTTPException(404, "Pelanggan tidak ditemukan")
    try:
        return await service.delete(kode_pelanggan, user_code, user_name)
    except Exception as exc:
        raise customer_http_exception(exc) from exc