import base64
import json
import os
import time
import uuid

import requests
from flask import current_app

# Labelary is used only for ZPL labels received from carrier APIs (DHL/DPD/Paket24).
_LABELARY_BASE = 'http://api.labelary.com/v1/printers/8dpmm/labels'
_LABELARY_DIMS = {
    'small': '2.17x1.26',
    'large': '3.94x5.91',
}


# ── File-based preview (dev mode) ─────────────────────────────────────────────

def _preview_dir() -> str:
    """Return (and create) the directory where preview PNGs are stored."""
    path = os.path.join(current_app.root_path, 'static', 'label_previews')
    os.makedirs(path, exist_ok=True)
    return path


def save_preview_label(png_b64: str, meta: dict) -> None:
    """Write one label PNG + a sidecar JSON to the preview directory."""
    name = f'{int(time.time() * 1000)}_{uuid.uuid4().hex[:6]}'
    d = _preview_dir()
    with open(os.path.join(d, name + '.png'), 'wb') as f:
        f.write(base64.b64decode(png_b64))
    with open(os.path.join(d, name + '.json'), 'w') as f:
        json.dump({'file': name + '.png', **meta}, f)


def list_preview_labels() -> list[dict]:
    """Return all saved labels (sorted oldest-first) with static URLs."""
    d = _preview_dir()
    items = []
    for fname in os.listdir(d):
        if not fname.endswith('.json'):
            continue
        try:
            with open(os.path.join(d, fname)) as f:
                meta = json.load(f)
            meta['url'] = f'/static/label_previews/{meta["file"]}'
            items.append(meta)
        except Exception:
            pass
    items.sort(key=lambda x: x.get('file', ''))
    return items


def clear_preview_labels() -> None:
    """Delete all files in the preview directory."""
    d = _preview_dir()
    for fname in os.listdir(d):
        try:
            os.remove(os.path.join(d, fname))
        except Exception:
            pass


# ── Helpers ────────────────────────────────────────────────────────────────────


def _mode() -> str:
    return (current_app.config.get('PRINT_MODE') or 'api').lower()


def _api_url() -> str:
    host = (current_app.config.get('PRINT_API_HOST') or '').strip()
    port = (current_app.config.get('PRINT_API_PORT') or '5001').strip()
    if not host:
        raise ValueError("PRINT_API_HOST not configured")
    return f"http://{host}:{port}"


def _render_zpl_to_png_b64(zpl: str, printer: str = 'large') -> str:
    dims = _LABELARY_DIMS.get(printer, _LABELARY_DIMS['large'])
    url = f'{_LABELARY_BASE}/{dims}/0/'
    resp = requests.post(
        url,
        data=zpl.encode('utf-8'),
        headers={'Accept': 'image/png', 'Content-Type': 'application/x-www-form-urlencoded'},
        timeout=10,
    )
    resp.raise_for_status()
    return base64.b64encode(resp.content).decode('utf-8')


def print_zpl(zpl: str, printer: str = 'small') -> bool:
    mode = _mode()
    if mode == 'preview':
        png_b64 = _render_zpl_to_png_b64(zpl, printer=printer)
        save_preview_label(png_b64, {'slot': printer, 'type': 'zpl'})
        return True
    headers = {'Content-Type': 'application/json'}
    api_key = current_app.config.get('PRINT_API_KEY')
    if api_key:
        headers['Authorization'] = f'Bearer {api_key}'
    resp = requests.post(
        f"{_api_url()}/print",
        json={'zpl': zpl, 'printer': printer},
        headers=headers,
        timeout=10,
    )
    resp.raise_for_status()
    return True


def print_label(img: 'PIL.Image.Image', printer: str = 'small',
                meta: dict = None, also_preview: bool = False) -> bool:
    from app.labels.render import image_to_zpl, image_to_png_b64

    mode = _mode()

    if mode == 'preview' or also_preview:
        png_b64 = image_to_png_b64(img)
        save_preview_label(png_b64, {
            'slot': printer,
            'type': 'label',
            **(meta or {}),
        })
        if mode == 'preview':
            return True

    # --- API mode ---
    zpl = image_to_zpl(img)
    headers = {'Content-Type': 'application/json'}
    api_key = current_app.config.get('PRINT_API_KEY')
    if api_key:
        headers['Authorization'] = f'Bearer {api_key}'
    resp = requests.post(
        f"{_api_url()}/print",
        json={'zpl': zpl, 'printer': printer},
        headers=headers,
        timeout=10,
    )
    resp.raise_for_status()
    return True


def print_pdf(pdf_bytes: bytes, printer: str = 'large') -> bool:
    mode = _mode()
    if mode == 'preview':
        save_preview_label(
            base64.b64encode(pdf_bytes).decode('utf-8'),
            {'slot': printer, 'type': 'pdf'},
        )
        return True
    headers = {'Content-Type': 'application/json'}
    api_key = current_app.config.get('PRINT_API_KEY')
    if api_key:
        headers['Authorization'] = f'Bearer {api_key}'
    pdf_b64 = base64.b64encode(pdf_bytes).decode('utf-8')
    resp = requests.post(
        f"{_api_url()}/print-pdf",
        json={'pdf': pdf_b64, 'printer': printer},
        headers=headers,
        timeout=30,
    )
    resp.raise_for_status()
    return True
