import json
import subprocess

from flask import Blueprint, current_app, redirect, request, url_for, jsonify
from flask_login import login_required, current_user
from werkzeug.security import generate_password_hash

from app import db as database
from app.services.shipping_mapping import DEFAULT_RULES, rules_from_json

settings_bp = Blueprint('settings', __name__)

# All fields editable from the Settings UI (used to populate the form on load)
ALL_SETTINGS_FIELDS = [
    # Printers
    'PRINT_MODE',
    'PRINTER_INVOICE',
    # Print API (companion app)
    'PRINT_API_HOST', 'PRINT_API_PORT', 'PRINT_API_KEY',
    # Notifications
    'MAKE_ORDER_NOTIFY_URL', 'MAKE_TEST_EMAIL', 'MAKE_TEST_MODE',
    # ERP / Shop
    'ERACUNI_URL', 'ERACUNI_USERNAME', 'ERACUNI_PASSWORD', 'ERACUNI_TOKEN',
    'SHOPIFY_DOMAIN', 'SHOPIFY_SHOP_DOMAIN',
    'SHOPIFY_CLIENT_ID', 'SHOPIFY_CLIENT_SECRET', 'SHOPIFY_GRANT_TYPE',
    'SHOPIFY_ACCESS_TOKEN',
    # Shipping
    'DHL_API_KEY', 'DHL_ACCOUNT_NUMBER',
    'DHL_SHIPPER_NAME', 'DHL_SHIPPER_STREET', 'DHL_SHIPPER_CITY',
    'DHL_SHIPPER_POSTAL', 'DHL_SHIPPER_COUNTRY', 'DHL_SHIPPER_EMAIL', 'DHL_SHIPPER_PHONE',
    'DPD_API_KEY', 'DPD_API_URL',
    'PAKET24_API_KEY', 'PAKET24_API_URL',
]


@settings_bp.route('/settings/save', methods=['POST'])
@login_required
def save_setting():
    """Save a single setting key/value pair."""
    db_path = current_app.config['DATABASE_PATH']
    data = request.get_json(silent=True) or {}
    key   = data.get('key', '').strip()
    value = data.get('value', '').strip()

    if not key or key not in ALL_SETTINGS_FIELDS:
        return jsonify(ok=False, error='Unknown key'), 400

    database.set_setting(key, value, db_path)
    current_app.config[key] = value
    return jsonify(ok=True, key=key)


@settings_bp.route('/settings/save-all', methods=['POST'])
@login_required
def save_all_settings():
    """Save multiple setting key/value pairs in one request."""
    db_path = current_app.config['DATABASE_PATH']
    data = request.get_json(silent=True) or {}
    pairs = data.get('settings', {})

    saved = []
    errors = []
    for key, value in pairs.items():
        key = key.strip()
        if not key or key not in ALL_SETTINGS_FIELDS:
            errors.append(key)
            continue
        database.set_setting(key, str(value).strip(), db_path)
        current_app.config[key] = str(value).strip()
        saved.append(key)

    if errors:
        return jsonify(ok=False, saved=saved, errors=errors), 400
    return jsonify(ok=True, saved=saved)


@settings_bp.route('/settings/printers/test', methods=['POST'])
@login_required
def test_printer():
    """Print a test label image on the requested printer slot ('small' or 'large')."""
    from app.services import printer as printer_svc
    from app.labels.render import new_label, load_font, LABEL_W, LARGE_LABEL_W, LARGE_LABEL_H

    data = request.get_json(silent=True) or {}
    slot = data.get('printer', 'small')
    if slot not in ('small', 'large'):
        return jsonify(ok=False, error='Invalid printer slot'), 400

    if slot == 'large':
        # 100×150 mm label
        w, h = LARGE_LABEL_W, LARGE_LABEL_H
        img, draw = new_label(w, h)
        draw.text((40, 40), 'Test Print', fill=0, font=load_font(72, bold=True))
        draw.text((40, 140), f'Printer: {slot.upper()}', fill=0, font=load_font(48))
        draw.rectangle([(40, 230), (w - 40, 235)], fill=0)
        draw.text((40, 250), 'Soldered Warehouse v3', fill=0, font=load_font(40))
    else:
        # 55×32 mm label
        img, draw = new_label()
        draw.text((20, 20), 'Test Print', fill=0, font=load_font(38, bold=True))
        draw.text((20, 72), f'Printer: {slot.upper()}', fill=0, font=load_font(22))
        draw.rectangle([(20, 114), (LABEL_W - 20, 117)], fill=0)
        draw.text((20, 124), 'Soldered Warehouse v3', fill=0, font=load_font(18))

    try:
        printer_svc.print_label(img, printer=slot, also_preview=True)
        return jsonify(ok=True)
    except Exception as e:
        return jsonify(ok=False, error=str(e)), 500


@settings_bp.route('/settings/printers/list')
@login_required
def list_printers():
    """Return available CUPS printer names on this machine."""
    try:
        result = subprocess.run(
            ['lpstat', '-p'],
            capture_output=True, text=True, timeout=5,
        )
        printers = []
        for line in result.stdout.splitlines():
            # "printer FooBar is idle."  ->  "FooBar"
            if line.startswith('printer '):
                parts = line.split()
                if len(parts) >= 2:
                    printers.append(parts[1])
        return jsonify(printers=printers)
    except Exception as e:
        return jsonify(printers=[], error=str(e))


@settings_bp.route('/settings/users/add', methods=['POST'])
@login_required
def add_user():
    db_path = current_app.config['DATABASE_PATH']
    email   = request.form.get('email', '').strip().lower()
    password = request.form.get('password', '').strip()
    confirm  = request.form.get('confirm', '').strip()

    if not email or not password:
        return redirect(url_for('orders.index_redirect') + '?tab=settings&error=missing_fields')
    if password != confirm:
        return redirect(url_for('orders.index_redirect') + '?tab=settings&error=password_mismatch')
    if database.get_user_by_email(email, db_path):
        return redirect(url_for('orders.index_redirect') + '?tab=settings&error=email_exists')

    database.create_user(email, generate_password_hash(password), db_path)
    return redirect(url_for('orders.index_redirect') + '?tab=settings&saved=users')


@settings_bp.route('/settings/users/<int:user_id>/delete', methods=['POST'])
@login_required
def delete_user(user_id):
    db_path = current_app.config['DATABASE_PATH']
    if user_id == current_user.id:
        return redirect(url_for('orders.index_redirect') + '?tab=settings&error=cant_delete_self')
    if len(database.get_all_users(db_path)) <= 1:
        return redirect(url_for('orders.index_redirect') + '?tab=settings&error=last_user')
    database.delete_user(user_id, db_path)
    return redirect(url_for('orders.index_redirect') + '?tab=settings&saved=users')


# ── Shipping rules ─────────────────────────────────────────────────────────────

SHIPPING_CARRIERS = ['DPD', 'DHL', 'Paket24', 'Pickup', 'GLS', 'FREE_SHIPPING']


@settings_bp.route('/settings/shipping-rules', methods=['GET'])
@login_required
def get_shipping_rules():
    db_path = current_app.config['DATABASE_PATH']
    rules_json = database.get_setting('SHIPPING_RULES_JSON', db_path)
    rules = rules_from_json(rules_json)
    return jsonify(rules=rules, carriers=SHIPPING_CARRIERS)


@settings_bp.route('/settings/shipping-rules', methods=['POST'])
@login_required
def save_shipping_rules():
    db_path = current_app.config['DATABASE_PATH']
    data = request.get_json(silent=True) or {}
    rules = data.get('rules', [])
    valid = [r for r in rules if (r.get('match') or '').strip() and r.get('carrier')]
    database.set_setting('SHIPPING_RULES_JSON', json.dumps(valid), db_path)
    return jsonify(ok=True, count=len(valid))


@settings_bp.route('/settings/shipping-rules/defaults', methods=['GET'])
@login_required
def shipping_rules_defaults():
    return jsonify(rules=DEFAULT_RULES, carriers=SHIPPING_CARRIERS)
