from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import StreamingResponse
from sqlalchemy import func
from sqlalchemy.orm import Session
from typing import List, Optional
from decimal import Decimal
from datetime import date
import io

from ..database import get_db
from ..models import CreditCustomer, CreditSale, CreditPayment, CustomerVehicle
from ..schemas import (
    CustomerOut, CustomerCreate, CustomerUpdate,
    CreditPaymentCreate, CreditPaymentOut,
    CustomerVehicleCreate, CustomerVehicleOut,
    LedgerEntry,
)
from ..dependencies import get_current_user

router = APIRouter()


def _recalc_balance(customer_id: int, db: Session):
    sales = db.query(func.coalesce(func.sum(CreditSale.amount), 0)).filter(
        CreditSale.customer_id == customer_id
    ).scalar()
    pmts = db.query(func.coalesce(func.sum(CreditPayment.amount), 0)).filter(
        CreditPayment.customer_id == customer_id
    ).scalar()
    db.query(CreditCustomer).filter(CreditCustomer.id == customer_id).update(
        {'current_balance': Decimal(str(sales)) - Decimal(str(pmts))}
    )


@router.get("", response_model=List[CustomerOut])
def list_customers(
    status: str = "ACTIVE",
    search: Optional[str] = None,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    q = db.query(CreditCustomer)
    if status != "ALL":
        q = q.filter(CreditCustomer.status == status)
    if search:
        q = q.filter(CreditCustomer.company_name.ilike(f"%{search}%"))
    return q.order_by(CreditCustomer.company_name).all()


@router.post("", response_model=CustomerOut)
def create_customer(
    body: CustomerCreate,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    customer = CreditCustomer(**body.model_dump())
    db.add(customer)
    db.commit()
    db.refresh(customer)
    return customer


@router.get("/{customer_id}", response_model=CustomerOut)
def get_customer(
    customer_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    c = db.query(CreditCustomer).filter(CreditCustomer.id == customer_id).first()
    if not c:
        raise HTTPException(status_code=404, detail="Customer not found")
    return c


@router.put("/{customer_id}", response_model=CustomerOut)
def update_customer(
    customer_id: int,
    body: CustomerUpdate,
    db: Session = Depends(get_db),
    user=Depends(get_current_user),
):
    c = db.query(CreditCustomer).filter(CreditCustomer.id == customer_id).first()
    if not c:
        raise HTTPException(status_code=404, detail="Customer not found")
    for field, value in body.model_dump(exclude_none=True).items():
        setattr(c, field, value)
    db.commit()
    db.refresh(c)
    # audit log imported lazily to avoid circular imports
    try:
        from .audit import log_audit
        log_audit(db, user.id, 'UPDATE', 'credit_customers', customer_id,
                  f"Updated customer {c.company_name}")
        db.commit()
    except Exception:
        pass
    return c


# ── Ledger ───────────────────────────────────────────────────────────────────

@router.get("/{customer_id}/ledger", response_model=List[LedgerEntry])
def get_ledger(
    customer_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    c = db.query(CreditCustomer).filter(CreditCustomer.id == customer_id).first()
    if not c:
        raise HTTPException(status_code=404, detail="Customer not found")

    sales = db.query(CreditSale).filter(CreditSale.customer_id == customer_id).all()
    pmts = db.query(CreditPayment).filter(CreditPayment.customer_id == customer_id).all()

    entries = []
    for s in sales:
        entries.append({
            'date': str(s.sale_date),
            'type': 'SALE',
            'ref': s.bill_no,
            'description': f"Sale - {s.fuel_type or ''} {s.vehicle_no or ''}".strip(),
            'debit': float(s.amount),
            'credit': 0.0,
            '_dt': s.sale_date,
        })
    for p in pmts:
        entries.append({
            'date': str(p.payment_date),
            'type': 'PAYMENT',
            'ref': p.reference_no or '',
            'description': f"Payment - {p.payment_method}",
            'debit': 0.0,
            'credit': float(p.amount),
            '_dt': p.payment_date,
        })

    entries.sort(key=lambda x: x['_dt'])

    balance = 0.0
    result = []
    for e in entries:
        balance += e['debit'] - e['credit']
        result.append(LedgerEntry(
            date=e['date'], type=e['type'], ref=e['ref'],
            description=e['description'], debit=e['debit'],
            credit=e['credit'], balance=round(balance, 2),
        ))
    return result


# ── Payments ─────────────────────────────────────────────────────────────────

@router.get("/{customer_id}/payments", response_model=List[CreditPaymentOut])
def list_payments(
    customer_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    return db.query(CreditPayment).filter(
        CreditPayment.customer_id == customer_id
    ).order_by(CreditPayment.payment_date.desc()).all()


@router.post("/{customer_id}/payments", response_model=CreditPaymentOut)
def create_payment(
    customer_id: int,
    body: CreditPaymentCreate,
    db: Session = Depends(get_db),
    user=Depends(get_current_user),
):
    c = db.query(CreditCustomer).filter(CreditCustomer.id == customer_id).first()
    if not c:
        raise HTTPException(status_code=404, detail="Customer not found")
    pmt = CreditPayment(
        customer_id=customer_id,
        received_by=user.id,
        **body.model_dump(),
    )
    db.add(pmt)
    db.flush()
    _recalc_balance(customer_id, db)
    db.commit()
    db.refresh(pmt)
    try:
        from .audit import log_audit
        log_audit(db, user.id, 'CREATE', 'credit_payments', pmt.id,
                  f"Payment Rs.{body.amount} from {c.company_name}")
        db.commit()
    except Exception:
        pass
    return pmt


@router.delete("/payments/{payment_id}")
def delete_payment(
    payment_id: int,
    db: Session = Depends(get_db),
    user=Depends(get_current_user),
):
    pmt = db.query(CreditPayment).filter(CreditPayment.id == payment_id).first()
    if not pmt:
        raise HTTPException(status_code=404, detail="Payment not found")
    customer_id = pmt.customer_id
    db.delete(pmt)
    db.flush()
    _recalc_balance(customer_id, db)
    db.commit()
    try:
        from .audit import log_audit
        log_audit(db, user.id, 'DELETE', 'credit_payments', payment_id,
                  f"Deleted payment {payment_id}")
        db.commit()
    except Exception:
        pass
    return {"ok": True}


# ── Vehicles ─────────────────────────────────────────────────────────────────

@router.get("/{customer_id}/vehicles", response_model=List[CustomerVehicleOut])
def list_vehicles(
    customer_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    return db.query(CustomerVehicle).filter(
        CustomerVehicle.customer_id == customer_id
    ).order_by(CustomerVehicle.vehicle_no).all()


@router.post("/{customer_id}/vehicles", response_model=CustomerVehicleOut)
def add_vehicle(
    customer_id: int,
    body: CustomerVehicleCreate,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    c = db.query(CreditCustomer).filter(CreditCustomer.id == customer_id).first()
    if not c:
        raise HTTPException(status_code=404, detail="Customer not found")
    v = CustomerVehicle(customer_id=customer_id, **body.model_dump())
    db.add(v)
    db.commit()
    db.refresh(v)
    return v


@router.delete("/vehicles/{vehicle_id}")
def delete_vehicle(
    vehicle_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    v = db.query(CustomerVehicle).filter(CustomerVehicle.id == vehicle_id).first()
    if not v:
        raise HTTPException(status_code=404, detail="Vehicle not found")
    db.delete(v)
    db.commit()
    return {"ok": True}


# ── PDF Statement ─────────────────────────────────────────────────────────────

@router.get("/{customer_id}/statement.pdf")
def customer_statement(
    customer_id: int,
    month: Optional[int] = None,
    year: Optional[int] = None,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    c = db.query(CreditCustomer).filter(CreditCustomer.id == customer_id).first()
    if not c:
        raise HTTPException(status_code=404, detail="Customer not found")

    try:
        from reportlab.lib.pagesizes import A4
        from reportlab.lib import colors
        from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
        from reportlab.lib.styles import getSampleStyleSheet
        from reportlab.lib.units import cm
    except ImportError:
        raise HTTPException(status_code=500, detail="reportlab not installed")

    sales_q = db.query(CreditSale).filter(CreditSale.customer_id == customer_id)
    pmts_q = db.query(CreditPayment).filter(CreditPayment.customer_id == customer_id)
    if month and year:
        from datetime import date as dt
        d_from = dt(year, month, 1)
        import calendar
        last_day = calendar.monthrange(year, month)[1]
        d_to = dt(year, month, last_day)
        sales_q = sales_q.filter(CreditSale.sale_date.between(d_from, d_to))
        pmts_q = pmts_q.filter(CreditPayment.payment_date.between(d_from, d_to))

    sales = sales_q.all()
    pmts = pmts_q.all()

    entries = []
    for s in sales:
        entries.append({'date': s.sale_date, 'type': 'SALE', 'ref': s.bill_no,
                        'desc': f"Fuel Sale{' - '+s.fuel_type if s.fuel_type else ''}",
                        'debit': float(s.amount), 'credit': 0.0})
    for p in pmts:
        entries.append({'date': p.payment_date, 'type': 'PAYMENT', 'ref': p.reference_no or '',
                        'desc': f"Payment ({p.payment_method})",
                        'debit': 0.0, 'credit': float(p.amount)})
    entries.sort(key=lambda x: x['date'])

    buf = io.BytesIO()
    doc = SimpleDocTemplate(buf, pagesize=A4, leftMargin=2*cm, rightMargin=2*cm,
                            topMargin=2*cm, bottomMargin=2*cm)
    styles = getSampleStyleSheet()
    story = []

    story.append(Paragraph(f"<b>Account Statement</b>", styles['Title']))
    story.append(Paragraph(f"Customer: {c.company_name}", styles['Normal']))
    if c.contact_person:
        story.append(Paragraph(f"Contact: {c.contact_person}", styles['Normal']))
    if month and year:
        import calendar
        story.append(Paragraph(f"Period: {calendar.month_name[month]} {year}", styles['Normal']))
    story.append(Spacer(1, 0.5*cm))

    headers = ['Date', 'Type', 'Reference', 'Description', 'Debit (Rs.)', 'Credit (Rs.)', 'Balance (Rs.)']
    data = [headers]
    balance = 0.0
    for e in entries:
        balance += e['debit'] - e['credit']
        data.append([
            str(e['date']), e['type'], e['ref'], e['desc'],
            f"{e['debit']:,.2f}" if e['debit'] else '-',
            f"{e['credit']:,.2f}" if e['credit'] else '-',
            f"{balance:,.2f}",
        ])
    data.append(['', '', '', 'Closing Balance', '', '', f"{balance:,.2f}"])

    col_widths = [2.2*cm, 1.8*cm, 2.5*cm, 4.5*cm, 2.5*cm, 2.5*cm, 2.5*cm]
    t = Table(data, colWidths=col_widths, repeatRows=1)
    t.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#1e293b')),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
        ('FONTSIZE', (0, 0), (-1, 0), 9),
        ('FONTSIZE', (0, 1), (-1, -1), 8),
        ('ROWBACKGROUNDS', (0, 1), (-1, -2), [colors.white, colors.HexColor('#f8fafc')]),
        ('BACKGROUND', (0, -1), (-1, -1), colors.HexColor('#fef9c3')),
        ('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
        ('GRID', (0, 0), (-1, -1), 0.3, colors.HexColor('#cbd5e1')),
        ('ALIGN', (4, 0), (-1, -1), 'RIGHT'),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('PADDING', (0, 0), (-1, -1), 4),
    ]))
    story.append(t)
    story.append(Spacer(1, 0.5*cm))
    story.append(Paragraph(f"Credit Limit: Rs. {float(c.credit_limit):,.2f} | "
                            f"Outstanding Balance: Rs. {float(c.current_balance):,.2f}", styles['Normal']))

    doc.build(story)
    buf.seek(0)
    fname = f"statement_{c.company_name.replace(' ', '_')}_{customer_id}.pdf"
    return StreamingResponse(buf, media_type="application/pdf",
                             headers={"Content-Disposition": f'attachment; filename="{fname}"'})
