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

from ..database import get_db
from ..models import Invoice, InvoiceLineItem, SystemSetting, CreditCustomer
from ..schemas import InvoiceCreate, InvoiceUpdate, InvoiceOut
from ..dependencies import get_current_user

router = APIRouter()


def _get_setting(db: Session, key: str, default: str = '') -> str:
    s = db.query(SystemSetting).filter(SystemSetting.setting_key == key).first()
    return s.setting_value if s else default


def _next_invoice_no(db: Session) -> str:
    """Return the next unused invoice number and advance the sequence setting."""
    prefix = _get_setting(db, 'invoice_prefix', 'INV')
    seq_str = _get_setting(db, 'invoice_next_seq', '1')
    seq = int(seq_str)
    year = date_type.today().year
    # Skip any numbers already in use (handles gaps from prior failed inserts)
    while True:
        candidate = f"{prefix}-{year}-{seq:06d}"
        exists = db.query(Invoice.id).filter(Invoice.invoice_no == candidate).first()
        if not exists:
            break
        seq += 1
    # Persist the next value
    setting = db.query(SystemSetting).filter(
        SystemSetting.setting_key == 'invoice_next_seq'
    ).first()
    if setting:
        setting.setting_value = str(seq + 1)
    return candidate


def _recalc(invoice: Invoice) -> None:
    subtotal = sum(li.amount for li in invoice.line_items)
    invoice.subtotal = subtotal
    invoice.vat_amount = (subtotal * invoice.vat_rate / Decimal('100')).quantize(Decimal('0.01'))
    invoice.total = invoice.subtotal + invoice.vat_amount


def _replace_line_items(db: Session, invoice: Invoice, items_data):
    for li in list(invoice.line_items):
        db.delete(li)
    for item in items_data:
        qty = item.quantity
        rate = item.unit_rate
        li = InvoiceLineItem(
            invoice_id=invoice.id,
            fuel_type=item.fuel_type,
            description=item.description,
            quantity=qty,
            unit_rate=rate,
            amount=(qty * rate).quantize(Decimal('0.01')),
        )
        db.add(li)
    db.flush()
    # Expire the cached relationship so _recalc re-queries the new rows
    db.expire(invoice, ['line_items'])


@router.get("/invoices/next-number")
def next_invoice_number(
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    return {"next_no": _next_invoice_no(db)}


@router.get("/invoices", response_model=List[InvoiceOut])
def list_invoices(
    status: Optional[str] = None,
    customer_id: Optional[int] = None,
    date_from: Optional[str] = None,
    date_to: Optional[str] = None,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    q = db.query(Invoice)
    if status:
        q = q.filter(Invoice.status == status)
    if customer_id:
        q = q.filter(Invoice.customer_id == customer_id)
    if date_from:
        q = q.filter(Invoice.invoice_date >= date_from)
    if date_to:
        q = q.filter(Invoice.invoice_date <= date_to)
    return q.order_by(Invoice.invoice_date.desc(), Invoice.id.desc()).all()


@router.post("/invoices", response_model=InvoiceOut)
def create_invoice(
    body: InvoiceCreate,
    db: Session = Depends(get_db),
    user=Depends(get_current_user),
):
    inv_no = _next_invoice_no(db)
    inv = Invoice(
        invoice_no=inv_no,
        invoice_type=body.invoice_type,
        customer_id=body.customer_id,
        shift_id=body.shift_id,
        invoice_date=body.invoice_date,
        due_date=body.due_date,
        vat_rate=body.vat_rate,
        payment_method=body.payment_method,
        customer_vat_no=body.customer_vat_no,
        notes=body.notes,
        created_by=user.id,
        subtotal=Decimal('0'),
        vat_amount=Decimal('0'),
        total=Decimal('0'),
    )
    db.add(inv)
    db.flush()
    _replace_line_items(db, inv, body.line_items)
    _recalc(inv)
    db.commit()
    db.refresh(inv)
    return inv


@router.get("/invoices/{invoice_id}", response_model=InvoiceOut)
def get_invoice(
    invoice_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    inv = db.query(Invoice).filter(Invoice.id == invoice_id).first()
    if not inv:
        raise HTTPException(404, "Invoice not found")
    return inv


@router.put("/invoices/{invoice_id}", response_model=InvoiceOut)
def update_invoice(
    invoice_id: int,
    body: InvoiceUpdate,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    inv = db.query(Invoice).filter(Invoice.id == invoice_id).first()
    if not inv:
        raise HTTPException(404, "Invoice not found")
    if inv.status not in ('DRAFT',):
        raise HTTPException(400, "Only DRAFT invoices can be edited")
    for k, v in body.model_dump(exclude={'line_items'}, exclude_none=True).items():
        setattr(inv, k, v)
    if body.line_items is not None:
        _replace_line_items(db, inv, body.line_items)
    _recalc(inv)
    db.commit()
    db.refresh(inv)
    return inv


@router.delete("/invoices/{invoice_id}")
def cancel_invoice(
    invoice_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    inv = db.query(Invoice).filter(Invoice.id == invoice_id).first()
    if not inv:
        raise HTTPException(404, "Invoice not found")
    if inv.status == 'CANCELLED':
        raise HTTPException(400, "Already cancelled")
    inv.status = 'CANCELLED'
    db.commit()
    return {"ok": True}


@router.post("/invoices/{invoice_id}/issue", response_model=InvoiceOut)
def issue_invoice(
    invoice_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    inv = db.query(Invoice).filter(Invoice.id == invoice_id).first()
    if not inv:
        raise HTTPException(404, "Invoice not found")
    if inv.status != 'DRAFT':
        raise HTTPException(400, "Only DRAFT invoices can be issued")
    if not inv.line_items:
        raise HTTPException(400, "Invoice must have at least one line item")
    inv.status = 'ISSUED'
    db.commit()
    db.refresh(inv)
    return inv


@router.post("/invoices/{invoice_id}/pay", response_model=InvoiceOut)
def pay_invoice(
    invoice_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    inv = db.query(Invoice).filter(Invoice.id == invoice_id).first()
    if not inv:
        raise HTTPException(404, "Invoice not found")
    if inv.status != 'ISSUED':
        raise HTTPException(400, "Only ISSUED invoices can be marked paid")
    inv.status = 'PAID'
    # Update credit customer balance if applicable
    if inv.customer_id:
        customer = db.query(CreditCustomer).filter(CreditCustomer.id == inv.customer_id).first()
        if customer:
            customer.current_balance = max(
                Decimal('0'),
                customer.current_balance - inv.total,
            )
    db.commit()
    db.refresh(inv)
    return inv


@router.get("/invoices/{invoice_id}/pdf")
def download_invoice_pdf(
    invoice_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    inv = db.query(Invoice).filter(Invoice.id == invoice_id).first()
    if not inv:
        raise HTTPException(404, "Invoice not found")

    from reportlab.lib.pagesizes import A4
    from reportlab.lib import colors
    from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
    from reportlab.lib.units import mm
    from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer

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

    # Station header
    station_name = _get_setting(db, 'station_name', 'Fuel Station')
    address      = _get_setting(db, 'station_address', '')
    phone        = _get_setting(db, 'station_phone', '')
    email        = _get_setting(db, 'station_email', '')
    vat_no       = _get_setting(db, 'station_vat_no', '')

    header_style = ParagraphStyle('h1', parent=styles['Heading1'], fontSize=14)
    story.append(Paragraph(station_name, header_style))
    if address:
        story.append(Paragraph(address, styles['Normal']))
    if phone:
        story.append(Paragraph(f"Tel: {phone}", styles['Normal']))
    if email:
        story.append(Paragraph(f"Email: {email}", styles['Normal']))
    if vat_no:
        story.append(Paragraph(f"VAT Reg: {vat_no}", styles['Normal']))
    story.append(Spacer(1, 6*mm))

    # Invoice info
    customer_name = ''
    if inv.customer_id:
        cust = db.query(CreditCustomer).filter(CreditCustomer.id == inv.customer_id).first()
        if cust:
            customer_name = cust.company_name
    inv_info = [
        ['Invoice No:', inv.invoice_no, 'Date:', str(inv.invoice_date)],
        ['Customer:', customer_name, 'Due Date:', str(inv.due_date or '')],
        ['Type:', inv.invoice_type, 'Status:', inv.status],
    ]
    if inv.customer_vat_no:
        inv_info.append(['Customer VAT:', inv.customer_vat_no, '', ''])
    t = Table(inv_info, colWidths=[35*mm, 65*mm, 25*mm, 55*mm])
    t.setStyle(TableStyle([('FONTSIZE', (0,0), (-1,-1), 9), ('BOTTOMPADDING', (0,0), (-1,-1), 2)]))
    story.append(t)
    story.append(Spacer(1, 6*mm))

    # Line items table
    headers = ['Description', 'Fuel', 'Qty (L)', 'Rate (Rs)', 'Amount (Rs)']
    rows = [headers]
    for li in inv.line_items:
        rows.append([
            li.description,
            li.fuel_type or '',
            f"{float(li.quantity):,.3f}",
            f"{float(li.unit_rate):,.2f}",
            f"{float(li.amount):,.2f}",
        ])
    items_table = Table(rows, colWidths=[70*mm, 20*mm, 25*mm, 30*mm, 35*mm])
    items_table.setStyle(TableStyle([
        ('BACKGROUND',   (0,0), (-1,0), colors.HexColor('#1e293b')),
        ('TEXTCOLOR',    (0,0), (-1,0), colors.white),
        ('FONTSIZE',     (0,0), (-1,-1), 9),
        ('GRID',         (0,0), (-1,-1), 0.5, colors.lightgrey),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, colors.HexColor('#f8fafc')]),
        ('ALIGN',        (2,0), (-1,-1), 'RIGHT'),
    ]))
    story.append(items_table)
    story.append(Spacer(1, 4*mm))

    # Totals
    totals = [
        ['', '', 'Subtotal:', f"Rs {float(inv.subtotal):,.2f}"],
        ['', '', f"VAT ({inv.vat_rate}%):", f"Rs {float(inv.vat_amount):,.2f}"],
        ['', '', 'TOTAL:', f"Rs {float(inv.total):,.2f}"],
    ]
    tot_table = Table(totals, colWidths=[70*mm, 20*mm, 55*mm, 35*mm])
    tot_table.setStyle(TableStyle([
        ('FONTSIZE',  (0,0), (-1,-1), 9),
        ('ALIGN',     (2,0), (-1,-1), 'RIGHT'),
        ('FONTNAME',  (2,2), (-1,2), 'Helvetica-Bold'),
        ('LINEABOVE', (2,2), (-1,2), 1, colors.black),
    ]))
    story.append(tot_table)

    if inv.notes:
        story.append(Spacer(1, 4*mm))
        story.append(Paragraph(f"Notes: {inv.notes}", styles['Normal']))

    doc.build(story)
    pdf_bytes = buf.getvalue()
    return Response(
        content=pdf_bytes,
        media_type='application/pdf',
        headers={'Content-Disposition': f'attachment; filename="invoice_{inv.invoice_no}.pdf"'},
    )
