"""
POS receipt printing — 50mm / 80mm thermal, A4, and continuous (dot matrix) paper profiles.
"""
from __future__ import annotations

import base64
from datetime import datetime
from typing import Any, Dict, List, Optional

from sqlalchemy.orm import Session, joinedload

from app.models.app_setting import AppSetting
from app.models.receipt import Receipt
from app.models.sales import Sale, SaleItem
from app.services.document_pdf_storage import normalize_customer_name_for_filename
from app.services.pos_receipt_service import PosReceiptService
from app.services.receipt_service import ReceiptService

# Paper profiles used across POS
PAPER_FORMATS: Dict[str, Dict[str, Any]] = {
    "50mm": {
        "code": "50mm",
        "label": "50mm thermal roll",
        "width_mm": 50,
        "width_chars": 32,
        "receipt_format": "50mm",
        "print_method": "browser",  # HTML + @page size → browser print dialog
    },
    "80mm": {
        "code": "80mm",
        "label": "80mm thermal roll",
        "width_mm": 80,
        "width_chars": 48,
        "receipt_format": "80mm",
        "print_method": "browser",
    },
    "a4": {
        "code": "a4",
        "label": "A4 paper",
        "width_mm": 210,
        "width_chars": 80,
        "receipt_format": "a4",
        "print_method": "browser",
    },
    "continuous": {
        "code": "continuous",
        "label": "Continuous / dot matrix",
        "width_mm": 210,
        "width_chars": 80,
        "receipt_format": "continuous",
        "print_method": "continuous_text",
    },
}

FORMAT_ALIASES = {
    "58mm": "50mm",
    "thermal_50mm": "50mm",
    "thermal_58mm": "50mm",
    "thermal_80mm": "80mm",
    "thermal": "80mm",
    "a4": "a4",
    "letter": "a4",
    "dot_matrix": "continuous",
    "continuous": "continuous",
    "impact": "continuous",
}


def normalize_paper_format(value: Optional[str]) -> str:
    if not value:
        return "80mm"
    key = str(value).strip().lower().replace(" ", "")
    return FORMAT_ALIASES.get(key, key if key in PAPER_FORMATS else "80mm")


def list_paper_formats() -> List[Dict[str, Any]]:
    return [
        {
            "code": p["code"],
            "label": p["label"],
            "width_mm": p["width_mm"],
            "width_chars": p["width_chars"],
            "print_method": p["print_method"],
        }
        for p in PAPER_FORMATS.values()
    ]


def get_default_paper_format(db: Session) -> str:
    try:
        inst = AppSetting.get_instance(db)
        return normalize_paper_format(getattr(inst, "default_receipt_format", None) or "80mm")
    except Exception:
        return "80mm"


def build_print_page_css(paper_format: str) -> str:
    """@page and body rules so browser print matches till roll / A4 / continuous."""
    fmt = normalize_paper_format(paper_format)
    if fmt == "50mm":
        return """
@page { size: 50mm auto; margin: 2mm; }
@media print {
  html, body { width: 50mm; margin: 0; padding: 0; }
  .receipt-root { width: 50mm !important; max-width: 50mm !important; margin: 0 auto !important; }
}
body { margin: 0; padding: 0; background: #fff; }
.receipt-root { box-sizing: border-box; }
"""
    if fmt == "80mm":
        return """
@page { size: 80mm auto; margin: 3mm; }
@media print {
  html, body { width: 80mm; margin: 0; padding: 0; }
  .receipt-root { width: 80mm !important; max-width: 80mm !important; margin: 0 auto !important; }
}
body { margin: 0; padding: 0; background: #fff; }
"""
    if fmt == "continuous":
        return """
@page { size: auto; margin: 5mm; }
body { margin: 0; font-family: 'Courier New', Courier, monospace; font-size: 10pt; }
.receipt-root { max-width: 80ch; margin: 0 auto; white-space: pre-wrap; }
"""
    return """
@page { size: A4 portrait; margin: 12mm; }
body { margin: 0; background: #fff; }
.receipt-root { max-width: 210mm; margin: 0 auto; }
"""


def wrap_print_html(body_html: str, paper_format: str, title: str = "Receipt") -> str:
    css = build_print_page_css(paper_format)
    return f"""<!DOCTYPE html>
<html><head>
<meta charset="utf-8">
<title>{title}</title>
<style>{css}</style>
</head>
<body>
<div class="receipt-root">
{body_html}
</div>
</body></html>"""


class PosPrintService:
    def __init__(self, db: Session):
        self.db = db
        self.receipt_service = ReceiptService(db)

    def build_print_job(
        self,
        *,
        receipt_id: Optional[str] = None,
        sale_id: Optional[str] = None,
        paper_format: Optional[str] = None,
        copy_type: str = "customer",
        regenerate_pdf: bool = False,
    ) -> Dict[str, Any]:
        receipt = None
        if receipt_id:
            receipt = self.receipt_service.get_receipt(receipt_id)
            if not receipt:
                raise ValueError("Receipt not found")
            sale_id = sale_id or receipt.sale_id

        if not sale_id and receipt and receipt.sale_id:
            sale_id = receipt.sale_id

        fmt = normalize_paper_format(paper_format or get_default_paper_format(self.db))
        profile = PAPER_FORMATS[fmt]

        sale = None
        if sale_id:
            sale = (
                self.db.query(Sale)
                .options(
                    joinedload(Sale.sale_items).joinedload(SaleItem.product),
                    joinedload(Sale.customer),
                    joinedload(Sale.branch),
                )
                .filter(Sale.id == sale_id)
                .first()
            )

        receipt_data = self._receipt_data_from_sale_or_receipt(sale, receipt)
        receipt_number = receipt_data.get("receipt_number") or "RCP-UNKNOWN"

        # HTML for browser printing
        inner_html = self.receipt_service._generate_html(receipt_data, profile["receipt_format"])
        print_html = wrap_print_html(inner_html, fmt, title=f"Receipt {receipt_number}")

        # PDF path — regenerate when format changes or missing
        pdf_path = receipt.pdf_path if receipt else None
        if sale and (regenerate_pdf or not pdf_path or fmt != normalize_paper_format(get_default_paper_format(self.db))):
            try:
                gen = self.receipt_service.generate_receipt(
                    str(sale.id),
                    receipt.created_by_user_id if receipt else "system",
                    profile["receipt_format"],
                )
                if gen.get("success"):
                    pdf_path = gen.get("pdf_path")
                    if receipt and gen.get("receipt_id") == receipt.id:
                        receipt.pdf_path = pdf_path
                        receipt.html_content = gen.get("html_content")
                        self.db.commit()
            except Exception:
                pass

        plain_text = None
        esc_pos_b64 = None

        if fmt == "continuous":
            plain_text = self.receipt_service.generate_continuous_text(
                receipt_data, width_chars=profile["width_chars"]
            )
        elif fmt in ("50mm", "80mm") and sale:
            try:
                pos_rcpt = PosReceiptService(self.db)
                pos_rcpt.set_paper_format(fmt)
                raw = pos_rcpt.generate_receipt(str(sale.id), copy_type)
                esc_pos_b64 = base64.b64encode(raw.encode("latin-1", errors="replace")).decode("ascii")
            except Exception:
                pass

        customer_label = normalize_customer_name_for_filename(receipt_data.get("customer_name"))

        return {
            "paper_format": fmt,
            "profile": profile,
            "receipt_id": receipt.id if receipt else None,
            "sale_id": sale_id,
            "receipt_number": receipt_number,
            "customer_name": customer_label,
            "print_method": profile["print_method"],
            "html": print_html,
            "pdf_path": pdf_path,
            "plain_text": plain_text,
            "esc_pos_base64": esc_pos_b64,
            "width_mm": profile["width_mm"],
            "width_chars": profile["width_chars"],
        }

    def _receipt_data_from_sale_or_receipt(
        self, sale: Optional[Sale], receipt: Optional[Receipt]
    ) -> Dict[str, Any]:
        if sale:
            from app.models.user import User
            from app.models.branch import Branch

            user = self.db.query(User).filter(User.id == sale.salesperson_id).first() if sale.salesperson_id else None
            branch = self.db.query(Branch).filter(Branch.id == sale.branch_id).first() if sale.branch_id else None
            customer = sale.customer
            items = sale.sale_items or []
            app_settings = None
            try:
                app_settings = self.db.query(AppSetting).first()
            except Exception:
                pass

            receipt_number = (receipt.receipt_number if receipt else None) or f"POS-{sale.reference or sale.id[:8]}"
            ts = sale.created_at or sale.sale_time or sale.date or datetime.utcnow()

            subtotal = sum(float(i.total_amount or 0) for i in items)
            items_data = []
            for item in items:
                items_data.append({
                    "name": item.product.name if item.product else "Item",
                    "quantity": item.quantity,
                    "unit_price": float(item.selling_price or 0),
                    "total": float(item.total_amount or 0),
                })

            return {
                "receipt_number": receipt_number,
                "date": ts.strftime("%Y-%m-%d %H:%M:%S"),
                "time": ts.strftime("%H:%M:%S"),
                "date_only": ts.strftime("%Y-%m-%d"),
                "company_name": app_settings.app_name if app_settings else "Company",
                "company_address": app_settings.address if app_settings else "",
                "company_phone": app_settings.phone if app_settings else "",
                "company_vat_number": app_settings.vat_registration_number if app_settings else "",
                "company_website": app_settings.website if app_settings else "",
                "branch_name": branch.name if branch else "",
                "branch_address": branch.address if branch else "",
                "customer_name": customer.name if customer else "Walk-in Customer",
                "cashier": (user.full_name or user.username) if user else "POS",
                "items": items_data,
                "subtotal": subtotal,
                "vat_amount": float(sale.total_vat_amount or 0),
                "total_amount": float(sale.total_amount or 0),
                "amount_tendered": float(sale.amount_tendered or 0) if sale.amount_tendered else None,
                "change_given": float(sale.change_given or 0) if sale.change_given else None,
                "payment_method": sale.payment_method or "cash",
                "sale_reference": sale.reference,
            }

        return {
            "receipt_number": receipt.receipt_number if receipt else "RECEIPT",
            "date_only": datetime.utcnow().strftime("%Y-%m-%d"),
            "time": datetime.utcnow().strftime("%H:%M:%S"),
            "company_name": "Company",
            "customer_name": "Walkin",
            "items": [],
            "subtotal": float(receipt.amount or 0) if receipt else 0,
            "total_amount": float(receipt.amount or 0) if receipt else 0,
            "payment_method": receipt.payment_method if receipt else "cash",
        }
