"""
销项发票 Service 层 — CRUD + 多条件查询 + 导出
"""
from __future__ import annotations

import io
import uuid
from datetime import date, datetime

from sqlalchemy import and_, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.exceptions import BizException, NotFoundException
from app.models.finance import FinSalesInvoice
from app.schemas.auth import CurrentUserPayload
from app.schemas.sales_invoice import (
    SalesInvoiceCreate,
    SalesInvoiceListResponse,
    SalesInvoiceResponse,
    SalesInvoiceUpdate,
)


def _to_response(inv: FinSalesInvoice) -> SalesInvoiceResponse:
    return SalesInvoiceResponse(
        id=inv.id,
        issuer=inv.issuer,
        receiver_customer_id=inv.receiver_customer_id,
        customer_name=inv.receiver_customer.name if inv.receiver_customer else None,
        invoice_number=inv.invoice_number,
        amount=float(inv.amount or 0),
        billing_date=inv.billing_date,
        payment_status=inv.payment_status,
        payment_date=inv.payment_date,
        payment_amount=float(inv.payment_amount or 0),
        remark=inv.remark,
        created_by=inv.created_by,
        creator_name=inv.creator.real_name if inv.creator else None,
        created_at=inv.created_at,
        updated_at=inv.updated_at,
    )


async def create_invoice(
    db: AsyncSession,
    user: CurrentUserPayload,
    body: SalesInvoiceCreate,
) -> SalesInvoiceResponse:
    # 检查发票号唯一性
    existing = (await db.execute(
        select(func.count()).select_from(FinSalesInvoice).where(
            FinSalesInvoice.invoice_number == body.invoice_number,
            FinSalesInvoice.is_deleted.is_(False),
        )
    )).scalar()
    if existing:
        raise BizException(message=f"发票号 {body.invoice_number} 已存在")

    inv = FinSalesInvoice(
        issuer=body.issuer,
        receiver_customer_id=body.receiver_customer_id,
        invoice_number=body.invoice_number,
        amount=body.amount,
        billing_date=body.billing_date,
        remark=body.remark,
        created_by=user.user_id,
    )
    db.add(inv)
    await db.commit()
    await db.refresh(inv)
    return _to_response(inv)


async def list_invoices(
    db: AsyncSession,
    page: int = 1,
    size: int = 20,
    customer_name: str | None = None,
    invoice_number: str | None = None,
    payment_status: str | None = None,
    start_date: date | None = None,
    end_date: date | None = None,
) -> SalesInvoiceListResponse:
    conditions = [FinSalesInvoice.is_deleted.is_(False)]

    if invoice_number:
        conditions.append(FinSalesInvoice.invoice_number.ilike(f"%{invoice_number}%"))
    if payment_status:
        conditions.append(FinSalesInvoice.payment_status == payment_status)
    if start_date:
        conditions.append(FinSalesInvoice.billing_date >= start_date)
    if end_date:
        conditions.append(FinSalesInvoice.billing_date <= end_date)

    where = and_(*conditions) if conditions else True

    total = (await db.execute(
        select(func.count()).select_from(FinSalesInvoice).where(where)
    )).scalar() or 0

    stmt = (
        select(FinSalesInvoice)
        .where(where)
        .order_by(FinSalesInvoice.billing_date.desc())
        .offset((page - 1) * size)
        .limit(size)
    )
    invoices = (await db.execute(stmt)).scalars().all()

    items = [_to_response(inv) for inv in invoices]

    # 如果有客户名称筛选，在 Python 层过滤（因为是 join 字段）
    if customer_name:
        items = [i for i in items if customer_name.lower() in (i.customer_name or "").lower()]
        total = len(items)

    return SalesInvoiceListResponse(
        total=total,
        items=items,
        page=page,
        size=size,
    )


async def get_invoice(
    db: AsyncSession,
    invoice_id: uuid.UUID,
) -> SalesInvoiceResponse:
    stmt = select(FinSalesInvoice).where(
        FinSalesInvoice.id == invoice_id,
        FinSalesInvoice.is_deleted.is_(False),
    )
    inv = (await db.execute(stmt)).scalar_one_or_none()
    if inv is None:
        raise NotFoundException("发票不存在或已被删除")
    return _to_response(inv)


async def update_invoice(
    db: AsyncSession,
    invoice_id: uuid.UUID,
    body: SalesInvoiceUpdate,
) -> SalesInvoiceResponse:
    stmt = select(FinSalesInvoice).where(
        FinSalesInvoice.id == invoice_id,
        FinSalesInvoice.is_deleted.is_(False),
    )
    inv = (await db.execute(stmt)).scalar_one_or_none()
    if inv is None:
        raise NotFoundException("发票不存在或已被删除")

    update_data = body.model_dump(exclude_unset=True)
    if not update_data:
        raise BizException(message="未提供任何需要更新的字段")

    update_data["updated_at"] = datetime.utcnow()
    await db.execute(
        update(FinSalesInvoice)
        .where(FinSalesInvoice.id == invoice_id)
        .values(**update_data)
    )
    await db.commit()

    updated = (await db.execute(
        select(FinSalesInvoice).where(FinSalesInvoice.id == invoice_id)
    )).scalar_one()
    return _to_response(updated)


async def export_invoices(
    db: AsyncSession,
    start_date: date | None = None,
    end_date: date | None = None,
) -> io.BytesIO:
    """导出指定时间段的发票汇总及回款追踪表"""
    from openpyxl import Workbook

    conditions = [FinSalesInvoice.is_deleted.is_(False)]
    if start_date:
        conditions.append(FinSalesInvoice.billing_date >= start_date)
    if end_date:
        conditions.append(FinSalesInvoice.billing_date <= end_date)

    stmt = (
        select(FinSalesInvoice)
        .where(and_(*conditions))
        .order_by(FinSalesInvoice.billing_date.desc())
    )
    invoices = (await db.execute(stmt)).scalars().all()

    wb = Workbook()
    ws = wb.active
    ws.title = "发票汇总及回款追踪"
    ws.append([
        "发票号", "开票方", "受票客户", "票面金额",
        "开票日期", "回款状态", "已回款金额", "回款日期", "备注"
    ])

    for inv in invoices:
        ws.append([
            inv.invoice_number,
            inv.issuer,
            inv.receiver_customer.name if inv.receiver_customer else "",
            float(inv.amount or 0),
            inv.billing_date.isoformat() if inv.billing_date else "",
            inv.payment_status,
            float(inv.payment_amount or 0),
            inv.payment_date.isoformat() if inv.payment_date else "",
            inv.remark or "",
        ])

    # 列宽
    col_widths = [20, 25, 25, 15, 15, 12, 15, 15, 30]
    for i, w in enumerate(col_widths, 1):
        ws.column_dimensions[chr(64 + i)].width = w

    buffer = io.BytesIO()
    wb.save(buffer)
    buffer.seek(0)
    return buffer
