"""Render warehouse labels from a JSON template definition.

Template format — a list of element dicts:
  {"type": "text",    "x": 10, "y": 57, "size": 14, "bold": false,
   "field": "sku"}                          ← dynamic field
  {"type": "text",    "x": 10, "y": 57, "size": 14, "bold": false,
   "value": "Static text"}                  ← static value
  {"type": "barcode",          "x": 10,  "y": 5,   "w": 340, "h": 48, "field": "order_number"}
  {"type": "barcode_vertical", "x": 390, "y": 13,  "bar_length": 228, "bar_thickness": 44,
   "field": "barcode", "symbology": "upc"}         ← 'code128' or 'upc'
  {"type": "logo",             "x": 360, "y": 5,   "w": 75, "h": 35}
  {"type": "line",             "x": 10,  "y": 100, "w": 420, "thickness": 2}
  {"type": "background",       "x": 0,   "y": 0,   "file": "sealed_bg.png"}
"""

import json
import os

from PIL import Image, ImageDraw

from app.labels.render import LABEL_W, LABEL_H, draw_barcode, draw_barcode_vertical, load_font

_STATIC_IMG = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'static', 'img')
_LOGO_PATH = os.path.join(_STATIC_IMG, 'soldered_logo.png')

# ── Default templates ──────────────────────────────────────────────────────────
DEFAULT_TEMPLATES: dict[str, list] = {
    'product': [
        {"id": "logo",    "type": "logo",    "x": 10,  "y": 5,   "w": 110, "h": 36},
        {"id": "sku",     "type": "text",    "x": 10,  "y": 46,  "size": 13, "bold": True,  "field": "sku"},
        {"id": "name",    "type": "text",    "x": 10,  "y": 63,  "size": 12, "bold": False, "field": "name", "max_w": 420},
        {"id": "loc",     "type": "text",    "x": 10,  "y": 118, "size": 16, "bold": True,  "field": "location"},
        # EAN-13 barcode horizontal at bottom — h=50 bars + ~13px HRT below = ~63px total, ends at y=243
        {"id": "barcode", "type": "barcode", "x": 10,  "y": 180, "w": 420, "h": 50, "field": "barcode", "symbology": "ean"},
    ],
    'product_sealed': [
        {"id": "bg",            "type": "background",     "x": 0,   "y": 0},
        {"id": "logo",          "type": "logo",           "x": 10,  "y": 6,   "w": 110, "h": 36},
        {"id": "sku",           "type": "text",           "x": 10,  "y": 48,  "size": 13, "bold": True,  "field": "sku"},
        {"id": "name",          "type": "text",           "x": 10,  "y": 66,  "size": 12, "bold": False, "field": "name"},
        {"id": "loc",           "type": "text",           "x": 10,  "y": 130, "size": 16, "bold": True,  "field": "location"},
        {"id": "sealed_amount", "type": "text",           "x": 430, "y": 195, "size": 36, "bold": True,  "field": "sealed_amount", "align": "right"},
    ],
    'digikey': [
        {"id": "logo",        "type": "logo",         "x": 10,  "y": 6,   "w": 110, "h": 36},
        {"id": "sku",         "type": "text",         "x": 10,  "y": 48,  "size": 13, "bold": True,  "field": "sku"},
        {"id": "name",        "type": "text",         "x": 10,  "y": 66,  "size": 12, "bold": False, "field": "name", "max_w": 340},
        {"id": "loc",         "type": "text",         "x": 10,  "y": 130, "size": 16, "bold": True,  "field": "location"},
        {"id": "data_matrix", "type": "data_matrix",  "x": 346, "y": 158, "size": 90},
    ],
    'order_summary': [
        # barcode h=44 (bars only); HRT text drawn ~13px below → ends ~y=61
        {"id": "barcode",  "type": "barcode", "x": 10,  "y": 5,   "w": 340, "h": 44, "field": "barcode_data", "hrt_field": "order_number"},
        {"id": "letter",   "type": "text",    "x": 382, "y": 2,   "size": 46, "bold": True,  "field": "letter"},
        # buyer wraps up to 3 lines (size 15, ~18px/line = 54px → ends ~y=116); max_w keeps it left of letter
        {"id": "buyer",    "type": "text",    "x": 10,  "y": 63,  "size": 15, "bold": True,  "field": "buyer_name", "max_w": 360},
        {"id": "items",    "type": "text",    "x": 10,  "y": 122, "size": 12, "bold": False, "field": "num_items"},
        {"id": "delivery", "type": "text",    "x": 10,  "y": 137, "size": 12, "bold": False, "field": "delivery"},
        {"id": "total",    "type": "text",    "x": 270, "y": 122, "size": 18, "bold": True,  "field": "total_eur"},
    ],
    'order_item': [
        # barcode h=44; HRT text drawn below → ends ~y=61
        {"id": "barcode",  "type": "barcode", "x": 10,  "y": 5,   "w": 355, "h": 44, "field": "barcode_data"},
        {"id": "letter",   "type": "text",    "x": 382, "y": 2,   "size": 46, "bold": True,  "field": "letter"},
        {"id": "sku",      "type": "text",    "x": 10,  "y": 63,  "size": 14, "bold": False, "field": "sku"},
        {"id": "url",      "type": "text",    "x": 10,  "y": 80,  "size": 11, "bold": False, "field": "product_url"},
        {"id": "location", "type": "text",    "x": 10,  "y": 94,  "size": 14, "bold": False, "field": "location"},
        {"id": "qty_name", "type": "text",    "x": 10,  "y": 112, "size": 18, "bold": True,  "field": "qty_name"},
    ],
}

# Sample data used to render previews in the editor
SAMPLE_FIELDS: dict[str, dict] = {
    'product': {
        'sku':      '333023',
        'name':     '2-channel relay board',
        'location': 'P4-11',
        'barcode':  '732388674107',
    },
    'product_sealed': {
        'sku':           '333023',
        'name':          '2-channel relay board',
        'location':      'P4-11',
        'sealed_amount': '10\u00d7',
    },
    'digikey': {
        'sku':      '333023',
        'name':     '2-channel relay board',
        'location': 'P4-11',
    },
    'order_summary': {
        'order_number': 'S1042',
        'barcode_data': 'S1042',
        'buyer_name':   'Ivan Horvat',
        'num_items':    '3 items',
        'delivery':     'DHL',
        'total_eur':    '49.99 USD',
        'letter':       'A',
    },
    'order_item': {
        'sku':          'SOLR-WV2023',
        'product_url':  'solde.red/SOLR-WV2023',
        'location':     'B-07',
        'qty_name':     '2\u00d7 Inkplate 10',
        'item_price':   '29.99 USD',
        'barcode_data': 'S1042SOLR-WV2023',
        'letter':       'A',
    },
}

# Field definitions injected into the editor UI (per template)
TEMPLATE_FIELDS: dict[str, list[dict]] = {
    'product': [
        {'id': 'sku',      'label': 'SKU'},
        {'id': 'name',     'label': 'Product Name'},
        {'id': 'location', 'label': 'Location'},
        {'id': 'barcode',  'label': 'Barcode'},
    ],
    'product_sealed': [
        {'id': 'sku',           'label': 'SKU'},
        {'id': 'name',          'label': 'Product Name'},
        {'id': 'location',      'label': 'Location'},
        {'id': 'sealed_amount', 'label': 'Sealed Amount'},
    ],
    'digikey': [
        {'id': 'sku',      'label': 'SKU'},
        {'id': 'name',     'label': 'Product Name'},
        {'id': 'location', 'label': 'Location'},
    ],
    'order_summary': [
        {'id': 'order_number', 'label': 'Order Number'},
        {'id': 'buyer_name',   'label': 'Buyer Name'},
        {'id': 'num_items',    'label': 'Item Count'},
        {'id': 'delivery',     'label': 'Delivery Method'},
        {'id': 'total_eur',    'label': 'Total'},
        {'id': 'letter',       'label': 'Batch Letter'},
    ],
    'order_item': [
        {'id': 'sku',          'label': 'SKU'},
        {'id': 'location',     'label': 'Warehouse Location'},
        {'id': 'qty_name',     'label': 'Qty \u00d7 Name'},
        {'id': 'item_price',   'label': 'Item Price'},
        {'id': 'barcode_data', 'label': 'Barcode Data'},
        {'id': 'letter',       'label': 'Batch Letter'},
    ],
}

# Fields that are optional — silently skipped when empty
_OPTIONAL_FIELDS = {'letter', 'item_price', 'barcode'}


# ── Renderer ───────────────────────────────────────────────────────────────────

# Scale factor used when rendering preview images for screen display.
PREVIEW_SCALE = 3


def _wrap_text(draw: ImageDraw.ImageDraw, text: str, font, max_width: int) -> list[str]:
    """Split *text* into lines that each fit within *max_width* pixels."""
    words = text.split(' ')
    lines: list[str] = []
    current = ''
    for word in words:
        test = (current + ' ' + word).strip()
        bbox = draw.textbbox((0, 0), test, font=font)
        if bbox[2] - bbox[0] <= max_width or not current:
            current = test
        else:
            lines.append(current)
            current = word
    if current:
        lines.append(current)
    return lines or [text]


def _render(elements: list, fields: dict, scale: int = 1) -> Image.Image:
    """Core renderer — always works in L (greyscale) mode, scaled by *scale*."""
    w = LABEL_W * scale
    h = LABEL_H * scale

    img  = Image.new('L', (w, h), 255)
    draw = ImageDraw.Draw(img)

    for elem in elements:
        etype = elem.get('type', '')
        x = int(elem.get('x', 0)) * scale
        y = int(elem.get('y', 0)) * scale

        field = elem.get('field')
        val   = str(fields.get(field) or '') if field else str(elem.get('value') or '')

        if field in _OPTIONAL_FIELDS and not val:
            continue

        if etype == 'text':
            if not val:
                continue
            size  = int(elem.get('size', 14)) * scale
            bold  = bool(elem.get('bold', False))
            font  = load_font(size, bold=bold)
            align = elem.get('align', 'left')

            elem_max_w = elem.get('max_w')
            max_w = (int(elem_max_w) * scale) if elem_max_w else (w - x - 8 * scale)
            lines  = _wrap_text(draw, val, font, max_w)
            cap_bb = draw.textbbox((0, 0), 'A', font=font)
            line_h = (cap_bb[3] - cap_bb[1]) + int(3 * scale)

            for i, line in enumerate(lines):
                lx = x
                if align in ('right', 'center'):
                    lbbox = draw.textbbox((0, 0), line, font=font)
                    tw = lbbox[2] - lbbox[0]
                    lx = x - tw if align == 'right' else x - tw // 2
                draw.text((lx, y + i * line_h), line, fill=0, font=font)

        elif etype == 'barcode':
            if not val:
                continue
            bw  = int(elem.get('w', LABEL_W - 20)) * scale
            bh  = int(elem.get('h', 48)) * scale
            sym = elem.get('symbology', 'code128')
            hrt_field = elem.get('hrt_field')
            hrt_val   = str(fields.get(hrt_field) or '') if hrt_field else None
            draw_barcode(img, val, x=x, y=y, width=bw, height=bh, symbology=sym,
                         hrt_text=hrt_val or None)

        elif etype == 'barcode_vertical':
            if not val:
                continue
            bl = int(elem.get('bar_length', 220)) * scale
            bt = int(elem.get('bar_thickness', 44)) * scale
            sym = elem.get('symbology', 'code128')
            draw_barcode_vertical(img, val, x=x, y=y, bar_length=bl, bar_thickness=bt, symbology=sym)

        elif etype == 'data_matrix':
            # Placeholder — renders a labelled box until data matrix is implemented
            size = int(elem.get('size', 90)) * scale
            draw.rectangle([(x, y), (x + size, y + size)], outline=0, width=max(1, scale))
            ph_font = load_font(max(8, size // 11), bold=False)
            draw.text((x + 4 * scale, y + size // 2 - 8 * scale), 'DATA',   fill=0, font=ph_font)
            draw.text((x + 4 * scale, y + size // 2 + 2 * scale), 'MATRIX', fill=0, font=ph_font)

        elif etype == 'background':
            fname = elem.get('file', 'sealed_bg.png')
            bg_path = os.path.join(_STATIC_IMG, fname)
            if os.path.exists(bg_path):
                bw = int(elem.get('w', LABEL_W)) * scale
                bh = int(elem.get('h', LABEL_H)) * scale
                bg = Image.open(bg_path).convert('L')
                bg = bg.resize((bw, bh), Image.LANCZOS)
                img.paste(bg, (x, y))

        elif etype == 'logo':
            _draw_logo(img, x, y, int(elem.get('w', 80)) * scale, int(elem.get('h', 40)) * scale)

        elif etype == 'line':
            lw = int(elem.get('w', LABEL_W - 20)) * scale
            t  = max(scale, int(elem.get('thickness', 2)) * scale)
            draw.rectangle([(x, y), (x + lw - 1, y + t - 1)], fill=0)

    return img


def render_template(elements: list, fields: dict) -> Image.Image:
    """Render a label for printing. Returns 440×256 greyscale PIL Image."""
    return _render(elements, fields, scale=1)


def render_template_preview(elements: list, fields: dict) -> Image.Image:
    """Render a label for screen preview at PREVIEW_SCALE×. Returns RGB PIL Image."""
    img = _render(elements, fields, scale=PREVIEW_SCALE)
    return img.convert('RGB')


def _draw_logo(canvas: Image.Image, x: int, y: int, w: int, h: int) -> None:
    """Paste the Soldered logo onto the canvas, composited on white."""
    if not os.path.exists(_LOGO_PATH):
        return
    logo = Image.open(_LOGO_PATH).convert('RGBA')
    bg = Image.new('RGBA', logo.size, (255, 255, 255, 255))
    bg.paste(logo, mask=logo.split()[3])
    logo = bg.convert('L').resize((w, h), Image.LANCZOS)
    logo = logo.point(lambda v: 0 if v < 128 else 255)
    canvas.paste(logo, (x, y))


# ── Persistence ────────────────────────────────────────────────────────────────

def get_template(name: str, db_path: str) -> list:
    """Load template from DB, falling back to DEFAULT_TEMPLATES."""
    from app.db import get_setting
    raw = get_setting(f'LABEL_TEMPLATE_{name}', db_path)
    if raw:
        try:
            tmpl = json.loads(raw)
            if name == 'order_summary':
                # Migrate saved templates: barcode field "order_number" → "barcode_data"
                # (order_number now holds the real number for HRT; barcode_data holds the hash)
                for elem in tmpl:
                    if elem.get('type') == 'barcode' and elem.get('field') == 'order_number':
                        elem['field'] = 'barcode_data'
                        elem.setdefault('hrt_field', 'order_number')
            return tmpl
        except Exception:
            pass
    return [dict(e) for e in DEFAULT_TEMPLATES.get(name, [])]


def save_template(name: str, elements: list, db_path: str) -> None:
    from app.db import set_setting
    set_setting(f'LABEL_TEMPLATE_{name}', json.dumps(elements), db_path)
