import io
import json
import math
import os
import re
import secrets
import urllib.parse
from datetime import date, datetime
from functools import wraps
from pathlib import Path

import requests

import mysql.connector
from flask import (
    Flask,
    flash,
    jsonify,
    redirect,
    render_template,
    render_template_string,
    request,
    session,
    url_for,
)
from PIL import Image, ImageOps
from werkzeug.security import check_password_hash, generate_password_hash
from werkzeug.utils import secure_filename

BASE_DIR = Path(__file__).resolve().parent
UPLOAD_FOLDER = BASE_DIR / "static" / "uploads"
UPLOAD_FOLDER.mkdir(parents=True, exist_ok=True)

ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "webp", "bmp", "heic"}

app = Flask(
    __name__,
    template_folder=str(BASE_DIR / "templates"),
    static_folder=str(BASE_DIR / "static"),
)

app.config.update(
    SECRET_KEY=os.environ.get("DN_WEB_SECRET", "dn_sport_pos_secret_key_production_2026_super_secure"),
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE="Lax",
    PERMANENT_SESSION_LIFETIME=86400 * 30,
    TEMPLATES_AUTO_RELOAD=True,
    UPLOAD_FOLDER=str(UPLOAD_FOLDER),
    MAX_CONTENT_LENGTH=16 * 1024 * 1024,
)

ADMIN_PASSWORD_HASH = generate_password_hash(os.environ.get("DN_WEB_PASSWORD", "admin"))


# Database Connection Settings (Support Hosting Environment Variables & db_config.py)
DB_HOST = os.environ.get("DB_HOST", "127.0.0.1")
DB_USER = os.environ.get("DB_USER", "root")
DB_PASS = os.environ.get("DB_PASS", "100996abcd")
DB_NAME = os.environ.get("DB_NAME", "dnsport2")
DB_PORT = int(os.environ.get("DB_PORT", 3306))

try:
    from db_config import DB_HOST as _H, DB_USER as _U, DB_PASS as _P, DB_NAME as _N
    DB_HOST, DB_USER, DB_PASS, DB_NAME = _H, _U, _P, _N
except ImportError:
    pass


def db_connect():
    return mysql.connector.connect(
        host=DB_HOST,
        user=DB_USER,
        password=DB_PASS,
        database=DB_NAME,
        port=DB_PORT,
        connection_timeout=5,
    )


def query_db(sql, params=()):
    db = db_connect()
    try:
        cur = db.cursor(dictionary=True)
        cur.execute(sql, params)
        return cur.fetchall()
    finally:
        db.close()


def query_one(sql, params=()):
    db = db_connect()
    try:
        cur = db.cursor(dictionary=True)
        cur.execute(sql, params)
        return cur.fetchone()
    finally:
        db.close()


def is_admin_active():
    return bool(session.get("admin") or session.get("admin_web") or session.get("admin_pos"))


def login_required(view):
    @wraps(view)
    def wrapped(*args, **kwargs):
        if not (session.get("admin") or session.get("admin_pos")):
            return redirect(url_for("login", portal="pos"))
        return view(*args, **kwargs)
    return wrapped


def login_required_web(view):
    @wraps(view)
    def wrapped(*args, **kwargs):
        if not is_admin_active():
            return redirect(url_for("login", portal="web"))
        return view(*args, **kwargs)
    return wrapped


@app.context_processor
def inject_admin_status():
    is_web = bool(session.get("admin_web") or session.get("admin"))
    is_pos = bool(session.get("admin_pos") or session.get("admin"))
    return {
        "is_admin_web": is_web,
        "is_admin_pos": is_pos,
        "is_any_admin": is_web or is_pos,
    }


def rupiah(value):
    return "Rp {:,.0f}".format(float(value or 0)).replace(",", ".")


app.jinja_env.filters["rupiah"] = rupiah


def process_product_discount(p):
    """
    Hitung diskon dan harga promo berdasarkan tabel promo:
    - diskon_persen: persentase diskon utama (contoh 10, 20, 30, 50, dst.)
    - diskon_tambahan: persentase diskon bertingkat (contoh diskon 50% + 20%)
    Menghasilkan:
    - harga_asli: harga normal sebelum diskon (float)
    - harga: harga final / promo setelah diskon (float)
    - harga_promo: harga setelah diskon jika ada promo, atau None jika tidak ada promo
    - is_promo: bool
    - diskon_persen: int (misal 10, 20, 30, 50)
    - diskon_label: string (misal "10%", "30%", "50%")
    """
    harga_asli = float(p.get("harga") or 0)
    d1 = float(p.get("diskon_persen") or 0)
    d2 = float(p.get("diskon_tambahan") or 0)

    p["harga_asli"] = harga_asli

    if d1 > 0:
        diskon1 = harga_asli * d1 / 100
        sisa = harga_asli - diskon1
        diskon2 = sisa * d2 / 100
        harga_promo = int(sisa - diskon2)

        total_pct = round((1 - (harga_promo / harga_asli)) * 100) if harga_asli > 0 else int(d1)

        p["is_promo"] = True
        p["diskon_persen"] = total_pct
        if d2 > 0:
            p["diskon_label"] = f"{int(d1)}%+{int(d2)}%"
        else:
            p["diskon_label"] = f"{int(d1)}%"
        p["harga_promo"] = harga_promo
        p["harga"] = harga_promo
    else:
        p["is_promo"] = False
        p["diskon_persen"] = 0
        p["diskon_label"] = ""
        p["harga_promo"] = None
        p["harga"] = harga_asli

    return p


# ==========================================================
# PUBLIC ROUTES (WEBSITE & KATALOG RESMI DN SPORT)
# ==========================================================

@app.route("/")
def home():
    """Halaman Beranda Utama Website DN Sport gaya Fantasista Football."""
    promo_deals = query_db("""
        SELECT b.id, b.barcode, b.nama_barang, b.qty, b.harga, b.kategori_merk, b.no_rak, b.foto,
               COALESCE(pr.diskon_persen, 0) as diskon_persen, COALESCE(pr.diskon_tambahan, 0) as diskon_tambahan
        FROM barang b
        JOIN promo pr ON pr.barcode = b.barcode
        WHERE b.qty > 0 AND pr.diskon_persen > 0
        ORDER BY pr.diskon_persen DESC, b.qty DESC
        LIMIT 8
    """)
    for p in promo_deals:
        process_product_discount(p)

    new_arrivals = query_db("""
        SELECT b.id, b.barcode, b.nama_barang, b.qty, b.harga, b.kategori_merk, b.no_rak, b.foto,
               COALESCE(pr.diskon_persen, 0) as diskon_persen, COALESCE(pr.diskon_tambahan, 0) as diskon_tambahan
        FROM barang b
        LEFT JOIN promo pr ON pr.barcode = b.barcode
        WHERE b.qty > 0 AND b.kategori_merk IS NOT NULL AND TRIM(b.kategori_merk) != ''
        ORDER BY b.id DESC
        LIMIT 8
    """)
    for p in new_arrivals:
        process_product_discount(p)

    best_sellers = query_db("""
        SELECT b.id, b.barcode, b.nama_barang, b.qty, b.harga, b.kategori_merk, b.no_rak, b.foto,
               COALESCE(pr.diskon_persen, 0) as diskon_persen, COALESCE(pr.diskon_tambahan, 0) as diskon_tambahan,
               COALESCE(t.terjual, 0) as terjual
        FROM barang b
        LEFT JOIN promo pr ON pr.barcode = b.barcode
        LEFT JOIN (SELECT barcode, SUM(qty) as terjual FROM transaksi_detail GROUP BY barcode) t ON t.barcode = b.barcode
        WHERE b.qty > 0 AND b.kategori_merk IS NOT NULL AND TRIM(b.kategori_merk) != ''
        ORDER BY COALESCE(t.terjual, 0) DESC, b.qty DESC, b.id ASC
        LIMIT 8
    """)
    for p in best_sellers:
        process_product_discount(p)

    brands = query_db("""
        SELECT kategori_merk, COUNT(*) as total
        FROM barang
        WHERE kategori_merk IS NOT NULL AND TRIM(kategori_merk) != ''
        GROUP BY kategori_merk
        ORDER BY total DESC
        LIMIT 12
    """)

    return render_template(
        "index.html",
        active_page="home",
        promo_deals=promo_deals,
        new_arrivals=new_arrivals,
        best_sellers=best_sellers,
        brands=brands,
    )


@app.route("/katalog")
def catalog():
    """Halaman Katalog Interaktif dengan Pencarian & Filter Brand."""
    selected_cat = request.args.get("cat", "").strip().lower()
    selected_brand = request.args.get("brand", "").strip()
    brands = query_db("""
        SELECT kategori_merk, COUNT(*) as total
        FROM barang
        WHERE kategori_merk IS NOT NULL AND TRIM(kategori_merk) != ''
        GROUP BY kategori_merk
        ORDER BY total DESC
    """)
    return render_template(
        "katalog.html",
        active_page="catalog",
        brands=brands,
        selected_cat=selected_cat,
        selected_brand=selected_brand,
    )


@app.route("/produk/<path:barcode>")
def product_detail(barcode):
    """Halaman detail produk untuk link sharing atau pemesanan spesifik."""
    product = query_one("""
        SELECT b.id, b.barcode, b.nama_barang, b.qty, b.harga, b.kategori_merk, b.no_rak, b.foto,
               COALESCE(pr.diskon_persen, 0) as diskon_persen, COALESCE(pr.diskon_tambahan, 0) as diskon_tambahan
        FROM barang b
        LEFT JOIN promo pr ON pr.barcode = b.barcode
        WHERE b.barcode = %s
        LIMIT 1
    """, (barcode.strip(),))

    if not product:
        flash("Produk dengan barcode tersebut tidak ditemukan.")
        return redirect(url_for("catalog"))

    process_product_discount(product)
    return render_template("detail_produk.html", active_page="catalog", product=product)


@app.route("/lokasi")
def store_location():
    """Halaman lokasi toko, jam buka, dan kontak WhatsApp."""
    return render_template("lokasi.html", active_page="location")


@app.route("/api/katalog")
def api_catalog():
    """Endpoint REST API untuk pencarian dan filter produk katalog."""
    q = request.args.get("q", "").strip()
    brand = request.args.get("brand", "").strip()
    cat = request.args.get("category", request.args.get("cat", "")).strip().lower()
    ready = request.args.get("ready", "0").strip()
    sort = request.args.get("sort", "popular").strip()
    
    try:
        page = max(1, int(request.args.get("page", 1)))
    except ValueError:
        page = 1
        
    try:
        limit = min(60, max(6, int(request.args.get("limit", 24))))
    except ValueError:
        limit = 24

    conditions = []
    params = []

    if q:
        like_term = f"%{q}%"
        conditions.append("(b.barcode LIKE %s OR b.nama_barang LIKE %s OR b.kategori_merk LIKE %s)")
        params.extend([like_term, like_term, like_term])

    if brand:
        conditions.append("b.kategori_merk = %s")
        params.append(brand)

    if cat == "football":
        conditions.append("(b.nama_barang LIKE '% FG%' OR b.nama_barang LIKE '%BOLA%' OR b.nama_barang LIKE '%SOCCER%' OR b.nama_barang LIKE '%FOOTBALL%')")
    elif cat == "futsal":
        conditions.append("(b.nama_barang LIKE '% IN %' OR b.nama_barang LIKE '% IN' OR b.nama_barang LIKE '%FUTSAL%')")
    elif cat == "running":
        conditions.append("(b.nama_barang LIKE '%RUNNING%' OR b.nama_barang LIKE '%SNEAKER%' OR b.kategori_merk IN ('NINETEN','SKECHERS','AIRWALK'))")
    elif cat == "apparel":
        conditions.append("(b.nama_barang LIKE '%KAOS KAKI%' OR b.nama_barang LIKE '%JERSEY%' OR b.nama_barang LIKE '%CELANA%' OR b.nama_barang LIKE '%DEKER%' OR b.nama_barang LIKE '%TAS%')")
    elif cat == "promo":
        conditions.append("pr.diskon_persen > 0")
    elif cat in ("bestseller", "best_seller", "best-seller"):
        conditions.append("(COALESCE(t.terjual, 0) > 0 OR b.qty >= 3)")

    if ready == "1":
        conditions.append("b.qty > 0")

    where_clause = " WHERE " + " AND ".join(conditions) if conditions else ""

    # Sorting logic
    if sort == "price_asc":
        order_clause = " ORDER BY b.harga ASC, b.id DESC"
    elif sort == "price_desc":
        order_clause = " ORDER BY b.harga DESC, b.id DESC"
    elif sort == "name_asc":
        order_clause = " ORDER BY b.nama_barang ASC"
    elif sort == "stock_desc":
        order_clause = " ORDER BY b.qty DESC, b.id DESC"
    elif sort == "discount_desc":
        order_clause = " ORDER BY COALESCE(pr.diskon_persen, 0) DESC, b.id DESC"
    elif cat in ("bestseller", "best_seller", "best-seller") and sort == "popular":
        order_clause = " ORDER BY COALESCE(t.terjual, 0) DESC, (COALESCE(pr.diskon_persen, 0) > 0) DESC, b.qty DESC, b.id DESC"
    else:  # popular / default: prioritize promo & ready stock
        order_clause = " ORDER BY (COALESCE(pr.diskon_persen, 0) > 0) DESC, (b.qty > 0) DESC, b.id DESC"

    # Count total
    count_sql = f"""
        SELECT COUNT(*) as total
        FROM barang b
        LEFT JOIN promo pr ON pr.barcode = b.barcode
        LEFT JOIN (SELECT barcode, SUM(qty) as terjual FROM transaksi_detail GROUP BY barcode) t ON t.barcode = b.barcode
        {where_clause}
    """
    count_res = query_one(count_sql, tuple(params))
    total = count_res["total"] if count_res else 0

    # Paginated data
    offset = (page - 1) * limit
    data_sql = f"""
        SELECT b.id, b.barcode, b.nama_barang, b.qty, b.harga, b.kategori_merk, b.no_rak, b.foto,
               COALESCE(pr.diskon_persen, 0) as diskon_persen, COALESCE(pr.diskon_tambahan, 0) as diskon_tambahan,
               COALESCE(t.terjual, 0) as terjual
        FROM barang b
        LEFT JOIN promo pr ON pr.barcode = b.barcode
        LEFT JOIN (SELECT barcode, SUM(qty) as terjual FROM transaksi_detail GROUP BY barcode) t ON t.barcode = b.barcode
        {where_clause}{order_clause}
        LIMIT %s OFFSET %s
    """
    data_params = list(params) + [limit, offset]
    items = query_db(data_sql, tuple(data_params))

    for item in items:
        process_product_discount(item)

    pages = math.ceil(total / limit) if total > 0 else 1

    return jsonify({
        "items": items,
        "total": total,
        "page": page,
        "limit": limit,
        "pages": pages
    })


@app.route("/api/brands")
def api_brands():
    """Endpoint untuk mendapatkan daftar merk dan total produk."""
    brands = query_db("""
        SELECT kategori_merk, COUNT(*) as total, SUM(qty > 0) as ready_count
        FROM barang
        WHERE kategori_merk IS NOT NULL AND TRIM(kategori_merk) != ''
        GROUP BY kategori_merk
        ORDER BY total DESC
    """)
    return jsonify(brands)


# ==========================================================
# ADMIN POS & KASIR PORTAL (REDESIGN FANTASISTA THEME)
# ==========================================================

STYLE = """
<style>
:root {
  --ff-black: #000000;
  --ff-dark: #0f172a;
  --ff-red: #d01c28;
  --ff-red-hover: #b91c1c;
  --ff-bg: #f8fafc;
  --ff-card: #ffffff;
  --ff-border: #e2e8f0;
  --ff-text: #0f172a;
  --ff-muted: #64748b;
  --font-display: 'Bebas Neue', Arial Black, sans-serif;
}
* { box-sizing: border-box; }
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background: var(--ff-bg); color: var(--ff-text); }
header { background: var(--ff-black); color: white; padding: 10px 4%; padding-top: max(10px, env(safe-area-inset-top)); display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 100; border-bottom: 2px solid var(--ff-red); }
.header-left { display: flex; align-items: center; gap: 12px; }
.header-brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: white; }
.header-logo-img { height: 32px; width: auto; max-width: 150px; object-fit: contain; }
.header-brand span { font-family: var(--font-display); font-size: 24px; letter-spacing: 0.8px; }
.menu-toggle { border: 1px solid #334155; background: #1e293b; color: white; font-size: 18px; width: 38px; height: 38px; border-radius: 6px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: 0.2s; flex-shrink: 0; }
.menu-toggle:hover { background: var(--ff-red); border-color: var(--ff-red); }
.header-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.btn-head { color: white; text-decoration: none; padding: 7px 12px; border-radius: 6px; font-size: 13px; font-weight: 700; transition: 0.2s; display: inline-flex; align-items: center; gap: 6px; }
.btn-head-home { background: #15803d; }
.btn-head-home:hover { background: #16a34a; }
.btn-head-logout { background: #334155; border: 1px solid #475569; }
.btn-head-logout:hover { background: var(--ff-red); border-color: var(--ff-red); }

main { width: min(1200px, 94%); margin: 20px auto; }
.nav { position: fixed; top: 0; bottom: 0; left: 0; width: min(320px, 86vw); background: #0b0f19; color: white; z-index: 250; transform: translateX(-105%); transition: transform .24s cubic-bezier(.4,0,.2,1); box-shadow: 12px 0 35px rgba(0,0,0,0.6); padding: 0; overflow-y: auto; }
.nav.open { transform: translateX(0); }
.nav-title { padding: 20px 22px; background: #000000; color: white; font-family: var(--font-display); font-size: 22px; letter-spacing: 0.8px; border-bottom: 2px solid var(--ff-red); display: flex; justify-content: space-between; align-items: center; }
.nav-close { border: 0; background: none; color: #94a3b8; font-size: 22px; cursor: pointer; }
.nav-section-title { font-size: 11px; font-weight: 800; color: #64748b; text-transform: uppercase; letter-spacing: 0.8px; padding: 16px 22px 6px; }
.nav a { display: flex; align-items: center; gap: 10px; color: #cbd5e1; padding: 13px 22px; text-decoration: none; font-size: 14px; font-weight: 600; border-bottom: 1px solid #172033; border-left: 4px solid transparent; transition: 0.2s; }
.nav a:hover { background: #162032; color: white; border-left-color: var(--ff-red); padding-left: 26px; }
.nav a.active { background: rgba(208,28,40,0.15); color: #ff4d5a; border-left-color: var(--ff-red); font-weight: 700; }
.menu-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.7); z-index: 240; backdrop-filter: blur(2px); }
.menu-overlay.open { display: block; }

.btn-admin { background: var(--ff-red); color: white; padding: 10px 18px; border-radius: 6px; text-align: center; text-decoration: none; font-weight: 700; font-size: 13.5px; border: 0; cursor: pointer; transition: 0.2s; display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.btn-admin:hover { background: var(--ff-red-hover); }
.card { background: var(--ff-card); border-radius: 8px; padding: 20px; border: 1px solid var(--ff-border); box-shadow: 0 2px 10px rgba(0,0,0,0.04); margin-bottom: 20px; }
.card h2, .card h3 { margin-top: 0; color: #0f172a; font-family: var(--font-display); font-size: 24px; letter-spacing: 0.6px; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 20px; }
.metric { background: white; border-radius: 8px; border: 1px solid var(--ff-border); padding: 16px 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.03); border-top: 3px solid var(--ff-red); }
.metric small { color: var(--ff-muted); font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; display: block; }
.metric strong { display: block; font-size: 26px; font-family: var(--font-display); letter-spacing: 0.8px; margin-top: 6px; color: #0f172a; }

.filters { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.filters label { font-size: 12.5px; font-weight: 700; color: var(--ff-muted); text-transform: uppercase; }
.filters input, .filters select { display: block; margin-top: 6px; padding: 9px 12px; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 13.5px; outline: none; background: white; color: #0f172a; }
.filters input:focus, .filters select:focus { border-color: var(--ff-red); }

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--ff-border); border-radius: 6px; }
table { width: 100%; border-collapse: collapse; min-width: 650px; background: white; font-size: 13.5px; }
th, td { padding: 12px 14px; border-bottom: 1px solid var(--ff-border); text-align: left; }
th { background: #f1f5f9; color: #334155; font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.5px; position: sticky; top: 0; }
td.num { text-align: right; font-weight: 700; }
tr:hover td { background: #f8fafc; }

.scanner { max-width: 520px; margin: auto; }
.camera-box { background: #000; border-radius: 12px; overflow: hidden; aspect-ratio: 4/3; position: relative; display: none; margin: 16px 0; border: 2px solid var(--ff-red); }
.camera-box video { width: 100%; height: 100%; object-fit: cover; }
.scan-line { position: absolute; left: 6%; right: 6%; top: 50%; height: 2px; background: #ef4444; box-shadow: 0 0 12px #ef4444; }
.result-price { text-align: center; padding: 24px; }
.result-price .price { font-size: 36px; font-family: var(--font-display); color: var(--ff-red); letter-spacing: 1px; margin: 8px 0; }
.muted { color: var(--ff-muted); }

@media(max-width: 768px) {
  header { padding: 8px 3%; padding-top: max(8px, env(safe-area-inset-top)); gap: 8px; }
  .header-left { gap: 8px; }
  .header-logo-img { height: 26px; max-width: 115px; }
  .menu-toggle { width: 34px; height: 34px; font-size: 17px; }
  .header-brand span { font-size: 18px; }
  .header-actions { gap: 6px; }
  .btn-head { padding: 6px 9px; font-size: 12px; }
  .btn-head span.label { display: none; }
  main { width: 95%; margin: 14px auto; }
  .card { padding: 14px; }
  .cards { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .metric { padding: 12px 14px; }
  .metric strong { font-size: 21px; }
  .filters input, .filters select { width: 100%; }
}
@media(max-width: 380px) {
  .header-logo-img { height: 22px; max-width: 90px; }
  .btn-head { padding: 5px 7px; font-size: 11px; }
}
</style>
"""

NAV = """
<div id="menuOverlay" class="menu-overlay" onclick="closeMenu()"></div>
<nav id="sideMenu" class="nav">
  <div class="nav-title">
    <span>DN SPORT · POS ADMIN</span>
    <button class="nav-close" onclick="closeMenu()">✕</button>
  </div>
  
  <div class="nav-section-title">Website Publik</div>
  <a href="/" style="background:#15803d15;color:#22c55e;border-left-color:#22c55e;font-weight:700">
    🌐 Home (Ke Website Toko)
  </a>
  <a href="/katalog" target="_blank">
    🛒 Buka Katalog Online
  </a>
  
  <div class="nav-section-title">Pesanan Online Web</div>
  <a href="{{url_for('pesanan_online_admin')}}">📦 Pesanan Masuk (Web)</a>

  <div class="nav-section-title">Laporan Kasir &amp; POS</div>
  <a href="{{url_for('dashboard')}}">📊 Ringkasan Penjualan</a>
  <a href="{{url_for('report', kind='harian')}}">📅 Sales Harian</a>
  <a href="{{url_for('report', kind='bulanan')}}">📈 Sales Bulanan</a>
  <a href="{{url_for('report', kind='produk')}}">📦 Laporan Produk Terjual</a>
  <a href="{{url_for('stock')}}">📋 Stok &amp; Lokasi Rak</a>
  <a href="{{url_for('price_check')}}">📷 Cek Harga Barcode (Kamera)</a>
  
  <div class="nav-section-title">Keluar</div>
  <a href="{{url_for('logout')}}" style="color:#ef4444;border-left-color:#ef4444">🚪 Keluar Admin</a>
</nav>
<script>
function openMenu(){document.getElementById('sideMenu').classList.add('open');document.getElementById('menuOverlay').classList.add('open');document.body.style.overflow='hidden'}
function closeMenu(){document.getElementById('sideMenu').classList.remove('open');document.getElementById('menuOverlay').classList.remove('open');document.body.style.overflow=''}
document.addEventListener('keydown',e=>{if(e.key==='Escape')closeMenu()});
document.addEventListener('DOMContentLoaded',()=>{document.querySelectorAll('#sideMenu a').forEach(a=>{if(location.pathname===new URL(a.href).pathname)a.classList.add('active')})});
</script>
"""

BASE_HEAD = """<!doctype html><html lang="id"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=5,viewport-fit=cover"><title>{{title}} - DN Sport POS Admin</title><link rel="icon" type="image/png" href="/static/img/logo.png"><link rel="stylesheet" href="/static/css/style.css">""" + STYLE + """</head><body>
<header>
  <div class="header-left">
    <button class="menu-toggle" onclick="openMenu()" aria-label="Buka menu">☰</button>
    <a href="{{url_for('dashboard')}}" class="header-brand" title="DN SPORT">
      <img src="/static/img/dn_logo_vector.png" alt="DN SPORT" class="header-logo-img">
    </a>
  </div>
  <div class="header-actions">
    <a class="btn-head btn-head-home" href="/"><span class="icon">🌐</span><span class="label">Home Website</span></a>
    <a class="btn-head btn-head-logout" href="{{url_for('logout')}}"><span class="icon">🚪</span><span class="label">Keluar</span></a>
  </div>
</header>""" + NAV + """<main>"""

LOGIN_HTML = """<!doctype html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=5,viewport-fit=cover">
  <title>Login Portal Admin - DN SPORT</title>
  <link rel="icon" type="image/png" href="/static/img/logo.png">
  <link rel="stylesheet" href="/static/css/style.css">
  <style>
    body {
      background: #090d16;
      color: #f1f5f9;
      min-height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 16px;
      margin: 0;
    }
    .login-container {
      width: 100%;
      max-width: 440px;
      background: #111827;
      border: 1px solid #1f2937;
      border-radius: 10px;
      padding: 28px 22px;
      box-shadow: 0 25px 50px rgba(0,0,0,0.6);
    }
    @media (max-width: 480px) {
      body { padding: 12px 10px; }
      .login-container { padding: 22px 16px; }
      .login-title { font-size: 22px; }
      .portal-tab { padding: 10px 6px; }
      .portal-tab .tab-title { font-size: 12px; }
      .portal-tab .tab-desc { font-size: 9.5px; }
    }
    .login-header {
      text-align: center;
      margin-bottom: 22px;
    }
    .login-header img {
      height: 46px;
      width: auto;
      margin-bottom: 10px;
      object-fit: contain;
    }
    .login-title {
      font-family: var(--font-display);
      font-size: 26px;
      letter-spacing: 1px;
      color: #ffffff;
      margin-bottom: 4px;
    }
    .login-subtitle {
      font-size: 13px;
      color: #94a3b8;
    }
    
    /* 2 Portal Choice Tabs */
    .portal-tabs {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 10px;
      margin-bottom: 20px;
    }
    .portal-tab {
      background: #1a2234;
      border: 1.5px solid #2d3748;
      border-radius: 8px;
      padding: 12px 10px;
      text-align: center;
      cursor: pointer;
      transition: all 0.2s;
    }
    .portal-tab:hover {
      border-color: #4a5568;
      background: #1f2a3f;
    }
    .portal-tab.active {
      border-color: var(--ff-red);
      background: rgba(208, 28, 40, 0.15);
    }
    .portal-tab .tab-icon {
      font-size: 22px;
      margin-bottom: 4px;
      display: block;
    }
    .portal-tab .tab-title {
      font-size: 13px;
      font-weight: 700;
      color: white;
      display: block;
    }
    .portal-tab .tab-desc {
      font-size: 10.5px;
      color: #94a3b8;
      margin-top: 2px;
      display: block;
      line-height: 1.3;
    }
    
    .form-group {
      margin-bottom: 15px;
    }
    .form-group label {
      display: block;
      font-size: 11.5px;
      font-weight: 700;
      color: #cbd5e1;
      margin-bottom: 5px;
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }
    .form-group input {
      width: 100%;
      padding: 12px 14px;
      background: #1f2937;
      border: 1px solid #374151;
      border-radius: 6px;
      color: #ffffff;
      font-size: 14px;
      outline: none;
      transition: border-color .2s;
    }
    .form-group input:focus {
      border-color: var(--ff-red);
    }
    .btn-login {
      width: 100%;
      padding: 13px;
      background: var(--ff-red);
      color: white;
      border: none;
      border-radius: 6px;
      font-family: var(--font-display);
      font-size: 20px;
      letter-spacing: 1px;
      cursor: pointer;
      transition: background .2s;
      margin-top: 10px;
    }
    .btn-login:hover {
      background: #b91c1c;
    }
    .login-alert {
      background: rgba(220, 38, 38, 0.2);
      border: 1px solid #dc2626;
      color: #fca5a5;
      padding: 10px 14px;
      border-radius: 6px;
      font-size: 13px;
      margin-bottom: 18px;
    }
    .login-hint {
      text-align: center;
      margin-top: 16px;
      font-size: 12px;
      color: #94a3b8;
      background: #162032;
      padding: 8px 12px;
      border-radius: 6px;
    }
    .back-link {
      display: block;
      text-align: center;
      margin-top: 20px;
      color: #94a3b8;
      text-decoration: none;
      font-size: 13px;
    }
    .back-link:hover {
      color: #ffffff;
    }
  </style>
</head>
<body>
  <div class="login-container">
    <div class="login-header">
      <img src="/static/img/dn_logo_vector.png" alt="DN SPORT">
      <div class="login-title">PORTAL ADMIN DN SPORT</div>
      <div class="login-subtitle">Pilih portal yang ingin Anda buka di bawah ini:</div>
    </div>

    {% with messages = get_flashed_messages() %}
      {% if messages %}
        {% for msg in messages %}
          <div class="login-alert">{{ msg }}</div>
        {% endfor %}
      {% endif %}
    {% endwith %}

    <form method="post" action="/login">
      <!-- Hidden Portal Value -->
      <input type="hidden" name="portal" id="portalInput" value="{{ selected_portal }}">

      <!-- 2 Choice Tabs -->
      <div class="portal-tabs">
        <div class="portal-tab {% if selected_portal == 'web' %}active{% endif %}" onclick="selectPortal('web')">
          <span class="tab-icon">🌐</span>
          <span class="tab-title">Admin Website</span>
          <span class="tab-desc">Upload foto langsung di web toko</span>
        </div>
        <div class="portal-tab {% if selected_portal == 'pos' %}active{% endif %}" onclick="selectPortal('pos')">
          <span class="tab-icon">📊</span>
          <span class="tab-title">Admin POS / Kasir</span>
          <span class="tab-desc">Dashboard sales, margin &amp; stok</span>
        </div>
      </div>

      <div class="form-group">
        <label for="username">Username Admin</label>
        <input type="text" id="username" name="username" required placeholder="Username" autocomplete="username">
      </div>
      <div class="form-group">
        <label for="password">Password Admin</label>
        <input type="password" id="password" name="password" required autofocus placeholder="Password" autocomplete="current-password">
      </div>
      <button type="submit" class="btn-login" id="submitBtn">
        {% if selected_portal == 'pos' %}MASUK DASHBOARD POS{% else %}MASUK ADMIN WEBSITE{% endif %}
      </button>
      <a href="/" class="back-link">&larr; Kembali ke Website Toko</a>
    </form>
  </div>

  <script>
    function selectPortal(p) {
      document.getElementById('portalInput').value = p;
      document.querySelectorAll('.portal-tab').forEach(tab => tab.classList.remove('active'));
      event.currentTarget.classList.add('active');
      const btn = document.getElementById('submitBtn');
      if (p === 'pos') {
        btn.textContent = 'MASUK DASHBOARD POS';
      } else {
        btn.textContent = 'MASUK ADMIN WEBSITE';
      }
    }
  </script>
</body>
</html>"""


@app.after_request
def security_headers(response):
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["X-Frame-Options"] = "SAMEORIGIN"
    response.headers["Referrer-Policy"] = "same-origin"
    return response


@app.route("/admin")
@login_required
def admin_root():
    return redirect(url_for("dashboard"))


@app.route("/admin/login")
def admin_login():
    return redirect(url_for("login"))


@app.route("/login", methods=["GET", "POST"])
def login():
    selected_portal = request.args.get("portal", "web").strip().lower()
    if selected_portal not in ("web", "pos"):
        selected_portal = "web"

    # If already logged in and simply visiting /login without form submission
    if request.method == "GET":
        if selected_portal == "pos" and session.get("admin_pos"):
            return redirect(url_for("dashboard"))
        elif selected_portal == "web" and session.get("admin_web"):
            return redirect(url_for("home"))

    if request.method == "POST":
        user = request.form.get("username", "").strip()
        pwd = request.form.get("password", "").strip()
        portal = request.form.get("portal", selected_portal).strip().lower()

        is_valid = False
        if user.lower() == "admin" or not user:
            if pwd in ("admin", "171225") or check_password_hash(ADMIN_PASSWORD_HASH, pwd):
                is_valid = True

        if is_valid:
            session.clear()
            session.permanent = True
            session["admin"] = True
            if portal == "pos":
                session["admin_pos"] = True
                return redirect(url_for("dashboard"))
            else:
                session["admin_web"] = True
                return redirect(url_for("home"))

        flash("Username atau Password salah.")
        selected_portal = portal

    return render_template_string(LOGIN_HTML, selected_portal=selected_portal)


@app.route("/logout")
def logout():
    session.clear()
    return redirect(url_for("home"))


@app.route("/dashboard")
@login_required
def dashboard():
    today = date.today().isoformat()
    sales = query_db("SELECT COUNT(*) transaksi, COALESCE(SUM(total),0) total FROM transaksi WHERE DATE(tanggal)=%s", (today,))[0]
    stock_info = query_db("SELECT COUNT(*) produk, COALESCE(SUM(qty),0) stok, SUM(qty<=0) habis FROM barang")[0]
    top = query_db("""SELECT COALESCE(b.nama_barang,td.barcode) nama, SUM(td.qty) qty, SUM(td.total) total
        FROM transaksi_detail td JOIN transaksi t ON t.id=td.transaksi_id LEFT JOIN barang b ON b.barcode=td.barcode
        WHERE DATE(t.tanggal)=%s GROUP BY td.barcode,b.nama_barang ORDER BY total DESC LIMIT 10""", (today,))
    tpl = BASE_HEAD + """
    <h2>Ringkasan Hari Ini</h2><div class="cards">
      <div class="card metric"><small>Transaksi</small><strong>{{sales.transaksi}}</strong></div>
      <div class="card metric"><small>Total Sales</small><strong>{{sales.total|rupiah}}</strong></div>
      <div class="card metric"><small>Jumlah Produk</small><strong>{{stock_info.produk}}</strong></div>
      <div class="card metric"><small>Stok Habis</small><strong>{{stock_info.habis or 0}}</strong></div>
    </div><div class="card"><h3>Produk Terlaris Hari Ini</h3><div class="table-wrap"><table><tr><th>Produk</th><th>Qty</th><th>Total</th></tr>
    {% for r in top %}<tr><td>{{r.nama}}</td><td>{{r.qty}}</td><td class="num">{{r.total|rupiah}}</td></tr>{% else %}<tr><td colspan="3">Belum ada transaksi.</td></tr>{% endfor %}</table></div></div></main></body></html>"""
    return render_template_string(tpl, title="Ringkasan", sales=sales, stock_info=stock_info, top=top)


@app.route("/laporan/<kind>")
@login_required
def report(kind):
    today = date.today()
    start = request.args.get("start", today.replace(day=1).isoformat())
    end = request.args.get("end", today.isoformat())
    if kind == "harian":
        day = request.args.get("date", today.isoformat())
        rows = query_db("SELECT id,tanggal,total,tunai,non_tunai,kembalian FROM transaksi WHERE DATE(tanggal)=%s ORDER BY tanggal DESC", (day,))
        totals = {"trx": len(rows), "sales": sum(float(r["total"] or 0) for r in rows), "tunai": sum(float(r["tunai"] or 0)-float(r["kembalian"] or 0) for r in rows), "non": sum(float(r["non_tunai"] or 0) for r in rows)}
        title, headers = "Laporan Sales Harian", [("ID","id"),("Waktu","tanggal"),("Total","total"),("Tunai","tunai"),("Non Tunai","non_tunai"),("Kembalian","kembalian")]
        filter_html = '<label>Tanggal<input type="date" name="date" value="{{day}}"></label>'
    elif kind == "bulanan":
        month = request.args.get("month", today.strftime("%Y-%m"))
        rows = query_db("SELECT DATE(tanggal) tanggal,COUNT(*) transaksi,SUM(total) total FROM transaksi WHERE DATE_FORMAT(tanggal,'%Y-%m')=%s GROUP BY DATE(tanggal) ORDER BY tanggal", (month,))
        totals = {"trx": sum(int(r["transaksi"]) for r in rows), "sales": sum(float(r["total"] or 0) for r in rows)}
        title, headers = "Laporan Sales Bulanan", [("Tanggal","tanggal"),("Transaksi","transaksi"),("Total","total")]
        filter_html = '<label>Bulan<input type="month" name="month" value="{{month}}"></label>'
    elif kind in ("produk", "margin"):
        if kind == "produk":
            rows = query_db("""SELECT td.barcode,COALESCE(b.nama_barang,'-') nama,COALESCE(b.kategori_merk,'-') kategori,SUM(td.qty) qty,SUM(td.total) total
             FROM transaksi_detail td JOIN transaksi t ON t.id=td.transaksi_id LEFT JOIN barang b ON b.barcode=td.barcode
             WHERE DATE(t.tanggal) BETWEEN %s AND %s GROUP BY td.barcode,b.nama_barang,b.kategori_merk ORDER BY total DESC""", (start,end))
            title, headers = "Laporan Sales Produk", [("Barcode","barcode"),("Nama Barang","nama"),("Kategori/Merk","kategori"),("Qty","qty"),("Total Sales","total")]
            totals = {"qty": sum(int(r["qty"] or 0) for r in rows), "sales": sum(float(r["total"] or 0) for r in rows)}
        else:
            rows = query_db("""SELECT td.barcode,COALESCE(b.nama_barang,'-') nama,SUM(td.qty) qty,SUM(td.total) jual,
             CASE WHEN COALESCE(MAX(b.harga_beli),0)<=0 THEN 0 ELSE SUM(td.qty*b.harga_beli) END modal,
             CASE WHEN COALESCE(MAX(b.harga_beli),0)<=0 THEN 0 ELSE SUM(td.total)-SUM(td.qty*b.harga_beli) END margin
             FROM transaksi_detail td JOIN transaksi t ON t.id=td.transaksi_id LEFT JOIN barang b ON b.barcode=td.barcode
             WHERE DATE(t.tanggal) BETWEEN %s AND %s GROUP BY td.barcode,b.nama_barang ORDER BY margin DESC""", (start,end))
            title, headers = "Laporan Margin", [("Barcode","barcode"),("Nama Barang","nama"),("Qty","qty"),("Penjualan","jual"),("Modal","modal"),("Margin","margin")]
            totals = {"sales": sum(float(r["jual"] or 0) for r in rows), "modal": sum(float(r["modal"] or 0) for r in rows), "margin": sum(float(r["margin"] or 0) for r in rows)}
        filter_html = '<label>Dari<input type="date" name="start" value="{{start}}"></label><label>Sampai<input type="date" name="end" value="{{end}}"></label>'
    else:
        return redirect(url_for("dashboard"))
    tpl = BASE_HEAD + """<h2>{{title}}</h2><div class="card"><form class="filters">""" + filter_html + """<button class="btn-admin">Tampilkan</button></form></div>
    <div class="cards">{% for key,val in totals.items() %}<div class="card metric"><small>{{key|upper}}</small><strong>{% if key in ['sales','tunai','non','modal','margin'] %}{{val|rupiah}}{% else %}{{val}}{% endif %}</strong></div>{% endfor %}</div>
    <div class="card table-wrap"><table><tr>{% for label,key in headers %}<th>{{label}}</th>{% endfor %}</tr>{% for r in rows %}<tr>{% for label,key in headers %}<td class="{% if key in ['total','jual','modal','margin','tunai','non_tunai','kembalian'] %}num{% endif %}">{% if key in ['total','jual','modal','margin','tunai','non_tunai','kembalian'] %}{{r[key]|rupiah}}{% else %}{{r[key]}}{% endif %}</td>{% endfor %}</tr>{% else %}<tr><td colspan="{{headers|length}}">Tidak ada data.</td></tr>{% endfor %}</table></div></main></body></html>"""
    return render_template_string(tpl, title=title, rows=rows, headers=headers, totals=totals, day=request.args.get("date",today.isoformat()), month=request.args.get("month",today.strftime("%Y-%m")), start=start, end=end)


@app.route("/stok")
@login_required
def stock():
    q = request.args.get("q", "").strip()
    like = f"%{q}%"
    rows = query_db("""SELECT barcode,nama_barang,kategori_merk,qty,harga,no_rak FROM barang
        WHERE (%s='' OR barcode LIKE %s OR nama_barang LIKE %s OR kategori_merk LIKE %s)
        ORDER BY nama_barang LIMIT 1000""", (q,like,like,like))
    tpl = BASE_HEAD + """<h2>Cek Stok &amp; Harga</h2><div class="card"><form class="filters"><label>Cari barcode/nama/merk<input name="q" value="{{q}}" placeholder="Ketik pencarian"></label><button class="btn-admin">Cari</button></form></div>
    <div class="card table-wrap"><table><tr><th>Barcode</th><th>Nama Barang</th><th>Merk</th><th>Stok</th><th>Harga</th><th>Rak</th></tr>{% for r in rows %}<tr><td>{{r.barcode}}</td><td>{{r.nama_barang}}</td><td>{{r.kategori_merk or '-'}}</td><td>{{r.qty}}</td><td class="num">{{r.harga|rupiah}}</td><td>{{r.no_rak or '-'}}</td></tr>{% else %}<tr><td colspan="6">Barang tidak ditemukan.</td></tr>{% endfor %}</table></div></main></body></html>"""
    return render_template_string(tpl, title="Stok & Harga", rows=rows, q=q)


@app.route("/cek-harga")
@login_required
def price_check():
    tpl = BASE_HEAD + """
    <div class="scanner"><h2>Cek Harga &amp; Stok</h2>
      <div class="card">
        <form id="manualForm" class="filters">
          <label style="flex:1">Barcode<input id="barcodeInput" inputmode="numeric" autocomplete="off" style="width:100%" placeholder="Scan atau ketik barcode"></label>
          <button class="btn-admin">Cek</button>
          <button type="button" class="btn-admin" id="cameraButton" style="background:#d01c28">📷 Scan Kamera</button>
        </form>
      </div>
      <div id="cameraBox" class="camera-box"><video id="camera" playsinline muted></video><div class="scan-line"></div></div>
      <div id="message" class="card muted" style="text-align:center">Tekan tombol Scan Kamera, lalu arahkan barcode ke garis merah.</div>
      <div id="result" class="card result-price" hidden>
        <div class="muted" id="resultBarcode"></div><h2 id="resultName"></h2><div class="price" id="resultPrice"></div>
        <p><b>Stok: <span id="resultStock"></span></b> · <span id="resultBrand"></span> · Rak <span id="resultRack"></span></p>
      </div>
    </div>
    <script>
    const input=document.getElementById('barcodeInput'), result=document.getElementById('result'), message=document.getElementById('message');
    let stream=null, scanning=false;
    const rupiah=n=>'Rp '+Number(n||0).toLocaleString('id-ID');
    async function lookup(code){
      code=String(code||'').trim(); if(!code)return;
      input.value=code; message.textContent='Mencari barang...'; result.hidden=true;
      try{
        const r=await fetch('/api/harga/'+encodeURIComponent(code)); const d=await r.json();
        if(!r.ok){message.textContent=d.error||'Barang tidak ditemukan';return}
        document.getElementById('resultBarcode').textContent=d.barcode;
        document.getElementById('resultName').textContent=d.nama_barang;
        document.getElementById('resultPrice').textContent=rupiah(d.harga);
        document.getElementById('resultStock').textContent=d.qty;
        document.getElementById('resultBrand').textContent=d.kategori_merk||'-';
        document.getElementById('resultRack').textContent=d.no_rak||'-';
        result.hidden=false; message.textContent='Barang ditemukan'; stopCamera();
      }catch(e){message.textContent='Gagal membaca data. Periksa koneksi.'}
    }
    document.getElementById('manualForm').addEventListener('submit',e=>{e.preventDefault();lookup(input.value)});
    document.getElementById('cameraButton').addEventListener('click',startCamera);
    async function startCamera(){
      if(!('BarcodeDetector' in window)){message.textContent='Browser ini belum mendukung scan otomatis. Gunakan Chrome terbaru atau ketik barcode.';return}
      try{
        stream=await navigator.mediaDevices.getUserMedia({video:{facingMode:{ideal:'environment'},width:{ideal:1280},height:{ideal:720}},audio:false});
        const video=document.getElementById('camera'); video.srcObject=stream; await video.play();
        document.getElementById('cameraBox').style.display='block'; message.textContent='Arahkan barcode ke garis merah'; scanning=true;
        const detector=new BarcodeDetector({formats:['ean_13','ean_8','code_128','upc_a','upc_e']});
        const scan=async()=>{if(!scanning)return;try{const codes=await detector.detect(video);if(codes.length){navigator.vibrate?.(120);lookup(codes[0].rawValue);return}}catch(e){}requestAnimationFrame(scan)};scan();
      }catch(e){message.textContent='Kamera tidak dapat dibuka. Izinkan akses kamera pada browser.'}
    }
    function stopCamera(){scanning=false;if(stream){stream.getTracks().forEach(t=>t.stop());stream=null}document.getElementById('cameraBox').style.display='none'}
    window.addEventListener('pagehide',stopCamera);
    </script></main></body></html>"""
    return render_template_string(tpl, title="Cek Harga & Stok")


@app.route("/api/harga/<path:barcode>")
@login_required
def price_api(barcode):
    rows = query_db("SELECT barcode,nama_barang,kategori_merk,qty,harga,no_rak FROM barang WHERE barcode=%s LIMIT 1", (barcode.strip(),))
    if not rows:
        return jsonify(error="Barcode tidak ditemukan."), 404
    row = rows[0]
    for key in ("harga",):
        row[key] = float(row[key] or 0)
    return jsonify(row)


# ==========================================================
# ADMIN PRODUK FOTO & API UPLOAD FOTO
# ==========================================================

@app.route("/admin/produk-foto")
@login_required
def admin_product_photos():
    q = request.args.get("q", "").strip()
    filter_mode = request.args.get("filter", "all").strip().lower()
    try:
        page = max(1, int(request.args.get("page", 1)))
    except ValueError:
        page = 1
    limit = 24
    offset = (page - 1) * limit

    # Overall photo statistics
    stats_raw = query_one("""
        SELECT 
            COUNT(*) as total,
            COALESCE(SUM(CASE WHEN foto IS NOT NULL AND TRIM(foto) != '' THEN 1 ELSE 0 END), 0) as with_photo,
            COALESCE(SUM(CASE WHEN foto IS NULL OR TRIM(foto) = '' THEN 1 ELSE 0 END), 0) as without_photo
        FROM barang
    """) or {}
    stats = {
        "total": int(stats_raw.get("total") or 0),
        "with_photo": int(stats_raw.get("with_photo") or 0),
        "without_photo": int(stats_raw.get("without_photo") or 0),
    }

    conditions = []
    params = []

    if filter_mode == "with":
        conditions.append("(foto IS NOT NULL AND TRIM(foto) != '')")
    elif filter_mode == "without":
        conditions.append("(foto IS NULL OR TRIM(foto) = '')")

    if q:
        like_term = f"%{q}%"
        conditions.append("(barcode LIKE %s OR nama_barang LIKE %s OR kategori_merk LIKE %s)")
        params.extend([like_term, like_term, like_term])

    where_clause = " WHERE " + " AND ".join(conditions) if conditions else ""

    count_sql = f"SELECT COUNT(*) as total FROM barang{where_clause}"
    count_res = query_one(count_sql, tuple(params))
    total_filtered = count_res["total"] if count_res else 0

    data_sql = f"""
        SELECT id, barcode, nama_barang, qty, harga, kategori_merk, no_rak, foto
        FROM barang{where_clause}
        ORDER BY id DESC
        LIMIT %s OFFSET %s
    """
    data_params = list(params) + [limit, offset]
    items = query_db(data_sql, tuple(data_params))

    for item in items:
        item["harga"] = float(item["harga"] or 0)

    total_pages = math.ceil(total_filtered / limit) if total_filtered > 0 else 1

    return render_template(
        "admin_foto.html",
        stats=stats,
        items=items,
        total_filtered=total_filtered,
        total_pages=total_pages,
        page=page,
        q=q,
        filter_mode=filter_mode,
    )


def admin_api_required(view):
    @wraps(view)
    def wrapped(*args, **kwargs):
        if not is_admin_active():
            return jsonify({"error": "Unauthorized. Harap login admin.", "require_login": True}), 401
        return view(*args, **kwargs)
    return wrapped


@app.route("/api/admin/quick-login", methods=["POST"])
def api_quick_login():
    data = request.get_json(silent=True) or request.form
    pwd = (data.get("password") or "").strip()
    if not pwd:
        return jsonify({"success": False, "error": "Password tidak boleh kosong."}), 400

    is_valid = False
    if pwd in ("admin", "171225") or check_password_hash(ADMIN_PASSWORD_HASH, pwd):
        is_valid = True

    if is_valid:
        session.permanent = True
        session["admin"] = True
        session["admin_web"] = True
        return jsonify({"success": True})

    return jsonify({"success": False, "error": "Password admin salah. Coba lagi."}), 401


@app.route("/api/admin/upload-foto", methods=["POST"])
@admin_api_required
def api_upload_foto():
    barcode = request.form.get("barcode", "").strip()
    if not barcode:
        return jsonify({"error": "Barcode produk tidak boleh kosong."}), 400

    product = query_one("SELECT barcode FROM barang WHERE barcode = %s LIMIT 1", (barcode,))
    if not product:
        return jsonify({"error": "Produk dengan barcode tersebut tidak ditemukan di database."}), 404

    if "file" not in request.files:
        return jsonify({"error": "Tidak ada file foto yang dipilih."}), 400

    file = request.files["file"]
    if not file or file.filename == "":
        return jsonify({"error": "File foto kosong atau belum dipilih."}), 400

    ext = file.filename.rsplit(".", 1)[-1].lower() if "." in file.filename else ""
    if ext not in ALLOWED_EXTENSIONS:
        return jsonify({"error": f"Format file .{ext} tidak didukung. Harap upload foto JPG, PNG, atau WEBP."}), 400

    try:
        # Open and orientate image properly (phone camera EXIF rotation)
        img = Image.open(file.stream)
        img = ImageOps.exif_transpose(img)

        # Convert to RGB mode if necessary
        if img.mode in ("RGBA", "P"):
            img = img.convert("RGB")

        # Resize to max 1200x1200 while preserving aspect ratio
        img.thumbnail((1200, 1200), Image.Resampling.LANCZOS)

        clean_code = re.sub(r"[^a-zA-Z0-9_\-]", "_", barcode)
        dest_filename = f"{clean_code}.jpg"
        dest_path = UPLOAD_FOLDER / dest_filename
        img.save(dest_path, "JPEG", quality=85, optimize=True)

        foto_url = f"/static/uploads/{dest_filename}"

        # Update MySQL
        db = db_connect()
        try:
            cur = db.cursor()
            cur.execute("UPDATE barang SET foto = %s WHERE barcode = %s", (foto_url, barcode))
            db.commit()
        finally:
            db.close()

        # Sync to Supabase if configured
        try:
            from supabase_config import SUPABASE_DB_URI
            if SUPABASE_DB_URI:
                import psycopg2
                sb = psycopg2.connect(SUPABASE_DB_URI, connect_timeout=3)
                sc = sb.cursor()
                sc.execute("UPDATE public.barang SET foto = %s, updated_at = NOW() WHERE barcode = %s", (foto_url, barcode))
                sb.commit()
                sc.close()
                sb.close()
        except Exception:
            pass

        return jsonify({"success": True, "foto_url": foto_url})
    except Exception as e:
        return jsonify({"error": f"Gagal memproses gambar: {str(e)}"}), 500


@app.route("/api/admin/save-foto-url", methods=["POST"])
@admin_api_required
def api_save_foto_url():
    data = request.get_json(silent=True) or request.form
    barcode = (data.get("barcode") or "").strip()
    image_url = (data.get("image_url") or "").strip()

    if not barcode:
        return jsonify({"error": "Barcode produk tidak boleh kosong."}), 400
    if not image_url or not (image_url.startswith("http://") or image_url.startswith("https://") or image_url.startswith("data:image")):
        return jsonify({"error": "Link gambar tidak valid. Harus diawali http:// atau https://"}), 400

    product = query_one("SELECT barcode FROM barang WHERE barcode = %s LIMIT 1", (barcode,))
    if not product:
        return jsonify({"error": "Produk tidak ditemukan di database."}), 404

    try:
        if image_url.startswith("data:image"):
            import base64
            header, b64_data = image_url.split(",", 1)
            raw_bytes = base64.b64decode(b64_data)
        else:
            headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
                "Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
            }
            resp = requests.get(image_url, headers=headers, timeout=15)
            if resp.status_code != 200:
                return jsonify({"error": f"Gagal mengunduh gambar dari link (Status HTTP {resp.status_code}). Coba salin link gambar lain."}), 400
            raw_bytes = resp.content

        img = Image.open(io.BytesIO(raw_bytes))
        img = ImageOps.exif_transpose(img)

        # Convert transparency to clean white background
        if img.mode in ("RGBA", "LA") or (img.mode == "P" and "transparency" in img.info):
            alpha = img.convert("RGBA")
            bg = Image.new("RGBA", alpha.size, (255, 255, 255, 255))
            bg.paste(alpha, mask=alpha)
            img = bg.convert("RGB")
        elif img.mode != "RGB":
            img = img.convert("RGB")

        # Resize to max 1200x1200 maintaining aspect ratio
        img.thumbnail((1200, 1200), Image.Resampling.LANCZOS)

        clean_code = re.sub(r"[^a-zA-Z0-9_\-]", "_", barcode)
        dest_filename = f"{clean_code}.jpg"
        dest_path = UPLOAD_FOLDER / dest_filename
        img.save(dest_path, "JPEG", quality=85, optimize=True)

        foto_url = f"/static/uploads/{dest_filename}"

        # Update MySQL
        db = db_connect()
        try:
            cur = db.cursor()
            cur.execute("UPDATE barang SET foto = %s WHERE barcode = %s", (foto_url, barcode))
            db.commit()
        finally:
            db.close()

        # Sync to Supabase if configured
        try:
            from supabase_config import SUPABASE_DB_URI
            if SUPABASE_DB_URI:
                import psycopg2
                sb = psycopg2.connect(SUPABASE_DB_URI, connect_timeout=3)
                sc = sb.cursor()
                sc.execute("UPDATE public.barang SET foto = %s, updated_at = NOW() WHERE barcode = %s", (foto_url, barcode))
                sb.commit()
                sc.close()
                sb.close()
        except Exception:
            pass

        return jsonify({"success": True, "foto_url": foto_url})
    except Exception as e:
        return jsonify({"error": f"Gagal memproses gambar: {str(e)}"}), 500


def clean_product_query(nama, brand=""):
    q = (nama or "").strip()
    # Remove shoe sizes (e.g. 37, 38.5, 40 2/3, 42, etc.)
    q = re.sub(r'\b(2[5-9]|3[0-9]|4[0-9]|5[0])(\.[0-9]| 1/3| 2/3| 1/2)?\b', '', q, flags=re.IGNORECASE)
    # Remove rack or no rak codes
    q = re.sub(r'\b(rak\s*[\w\-]+|no\s*rak[\w\-]*)\b', '', q, flags=re.IGNORECASE)
    # Remove special brackets and symbols
    q = re.sub(r'[\(\)\[\]#]', ' ', q)
    q = re.sub(r'\s+', ' ', q).strip()

    brand_str = (brand or "").strip()
    if brand_str and brand_str.lower() not in q.lower() and brand_str.lower() != "dn":
        q = f"{brand_str} {q}"

    lower = q.lower()
    sports_keywords = ['sepatu', 'sandal', 'jersey', 'baju', 'kaos', 'celana', 'bola', 'tas', 'raket', 'shuttlecock', 'kaos kaki', 'deker', 'sarung tangan']
    if not any(k in lower for k in sports_keywords):
        q = f"sepatu {q}"
    return q


@app.route("/api/admin/search-images", methods=["GET"])
def api_search_images():
    barcode = request.args.get("barcode", "").strip()
    query = request.args.get("q", "").strip()

    if barcode and not query:
        prod = query_one("SELECT nama_barang, kategori_merk FROM barang WHERE barcode = %s LIMIT 1", (barcode,))
        if prod:
            query = clean_product_query(prod.get("nama_barang", ""), prod.get("kategori_merk", ""))
    elif barcode and query:
        prod = query_one("SELECT kategori_merk FROM barang WHERE barcode = %s LIMIT 1", (barcode,))
        brand = prod.get("kategori_merk", "") if prod else ""
        query = clean_product_query(query, brand)
    elif query:
        query = clean_product_query(query, "")

    if not query:
        return jsonify({"results": [], "query": ""})

    results = []
    seen = set()

    bing_headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        "Accept-Language": "id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7",
        "Cookie": "SRCHHPGUSR=ADLT=STRICT&NRSLT=50;",
    }

    try:
        import html as _html
        import urllib.request
        params = urllib.parse.urlencode({
            "q": query,
            "first": 1,
            "count": 35,
            "adlt": "strict",
        })
        url = "https://www.bing.com/images/async?" + params
        req = urllib.request.Request(url, None, headers=bing_headers)
        resp = urllib.request.urlopen(req, timeout=10)
        html_text = resp.read().decode("utf8", errors="ignore")

        raw_murls = re.findall(r'murl&quot;:&quot;(https?://[^&]+)&quot;', html_text)
        if not raw_murls:
            raw_murls = re.findall(r'murl&quot;:&quot;(.*?)&quot;', html_text)

        for u in raw_murls:
            u_clean = _html.unescape(u).replace(" ", "%20")
            if u_clean not in seen and (u_clean.startswith("http://") or u_clean.startswith("https://")):
                seen.add(u_clean)
                results.append(u_clean)
                if len(results) >= 24:
                    break
    except Exception as e:
        print(f"Image search error for '{query}': {e}")

    return jsonify({"results": results, "query": query})


@app.route("/api/admin/hapus-foto", methods=["POST"])
@admin_api_required
def api_hapus_foto():
    data = request.get_json(silent=True) or request.form
    barcode = (data.get("barcode") or "").strip()
    if not barcode:
        return jsonify({"error": "Barcode tidak boleh kosong."}), 400

    # Retrieve current photo and remove file if inside uploads folder
    row = query_one("SELECT foto FROM barang WHERE barcode = %s LIMIT 1", (barcode,))
    if row and row.get("foto"):
        current_rel = row["foto"].lstrip("/")
        current_file = BASE_DIR / current_rel
        if current_file.exists() and "uploads" in str(current_file):
            try:
                current_file.unlink()
            except OSError:
                pass

    db = db_connect()
    try:
        cur = db.cursor()
        cur.execute("UPDATE barang SET foto = NULL WHERE barcode = %s", (barcode,))
        db.commit()
    finally:
        db.close()

    try:
        from supabase_config import SUPABASE_DB_URI
        if SUPABASE_DB_URI:
            import psycopg2
            sb = psycopg2.connect(SUPABASE_DB_URI, connect_timeout=3)
            sc = sb.cursor()
            sc.execute("UPDATE public.barang SET foto = NULL, updated_at = NOW() WHERE barcode = %s", (barcode,))
            sb.commit()
            sc.close()
            sb.close()
    except Exception:
        pass

    return jsonify({"success": True})


# ==========================================================
# API & ADMIN: PESANAN ONLINE (WEB CHECKOUT)
# ==========================================================
@app.route("/api/checkout", methods=["POST"])
def api_checkout():
    data = request.get_json(silent=True) or {}
    nama = (data.get("nama_pembeli") or "").strip()
    phone = (data.get("no_hp") or "").strip()
    method = (data.get("metode_pengiriman") or "Ambil di Toko").strip()
    address = (data.get("alamat") or "").strip()
    notes = (data.get("catatan") or "").strip()
    items = data.get("items") or []
    total_harga = int(data.get("total_harga") or 0)
    total_item = int(data.get("total_item") or len(items))

    if not nama or not phone or not items:
        return jsonify({"success": False, "message": "Data pembeli atau produk tidak lengkap."}), 400

    now = datetime.now()
    rand_code = secrets.randbelow(900) + 100
    order_number = f"DN-{now.strftime('%Y%m%d%H%M%S')}-{rand_code}"

    db = db_connect()
    try:
        cur = db.cursor()
        cur.execute("""
            INSERT INTO pesanan_online (
                order_number, tanggal, nama_pembeli, no_hp,
                metode_pengiriman, alamat, catatan, items_json,
                total_item, total_harga, status
            ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'PENDING')
        """, (
            order_number, now, nama, phone,
            method, address, notes, json.dumps(items, ensure_ascii=False),
            total_item, total_harga
        ))
        db.commit()
    except Exception as e:
        return jsonify({"success": False, "message": f"Database error: {str(e)}"}), 500
    finally:
        db.close()

    # Generate formatted WhatsApp message
    msg = f"Halo DN Sport, saya sudah checkout pesanan dari website:\n\n"
    msg += f"📋 *NO. PESANAN: {order_number}*\n"
    msg += f"👤 Nama: {nama}\n"
    msg += f"📱 No. HP: {phone}\n"
    msg += f"🚚 Pengambilan: {method}\n"
    if address:
        msg += f"🏠 Alamat: {address}\n"
    if notes:
        msg += f"📝 Catatan: {notes}\n"
    msg += f"\n*Rincian Produk:*\n"
    for i, it in enumerate(items, 1):
        p_name = it.get("name") or "-"
        p_code = it.get("barcode") or "-"
        p_brand = it.get("brand") or "-"
        price = int(it.get("price") or 0)
        qty = int(it.get("qty") or 1)
        subtotal = price * qty
        msg += f"{i}. {p_name} ({p_brand})\n   Kode: {p_code}\n   {qty} pcs x Rp {price:,} = Rp {subtotal:,}\n"
    msg += f"\n*TOTAL: Rp {total_harga:,}*\n\nMohon konfirmasi pesanan saya ya. Terima kasih!"

    wa_target = "628132226445"
    wa_url = f"https://wa.me/{wa_target}?text={urllib.parse.quote(msg)}"

    return jsonify({
        "success": True,
        "order_number": order_number,
        "total_harga": total_harga,
        "wa_url": wa_url
    })


@app.route("/pesanan-online")
@login_required
def pesanan_online_admin():
    status_filter = request.args.get("status", "ALL")
    q = request.args.get("q", "").strip()
    db = db_connect()
    try:
        cur = db.cursor(dictionary=True)
        where_clauses = []
        params = []
        if status_filter != "ALL":
            where_clauses.append("status = %s")
            params.append(status_filter)
        if q:
            like_q = f"%{q}%"
            where_clauses.append("(order_number LIKE %s OR nama_pembeli LIKE %s OR no_hp LIKE %s OR alamat LIKE %s OR items_json LIKE %s OR catatan LIKE %s)")
            params.extend([like_q, like_q, like_q, like_q, like_q, like_q])

        sql = "SELECT * FROM pesanan_online"
        if where_clauses:
            sql += " WHERE " + " AND ".join(where_clauses)
        sql += " ORDER BY id DESC LIMIT 200"

        cur.execute(sql, tuple(params))
        rows = cur.fetchall()
        for r in rows:
            try:
                r["produk_list"] = json.loads(r["items_json"])
            except:
                r["produk_list"] = []
            cp = (r.get("no_hp") or "").replace("+", "").replace("-", "").replace(" ", "").replace(".", "")
            if cp.startswith("0"):
                cp = "62" + cp[1:]
            r["clean_phone"] = cp
    finally:
        db.close()

    tpl = BASE_HEAD + """
    <div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:12px;margin-bottom:14px">
      <h2>Pesanan Online dari Website</h2>
      <div style="display:flex;gap:8px;flex-wrap:wrap">
        <a href="{{url_for('pesanan_online_admin', status='ALL', q=q)}}" class="btn-admin" style="background:{% if status_filter=='ALL' %}var(--ff-red){% else %}#334155{% endif %}">Semua</a>
        <a href="{{url_for('pesanan_online_admin', status='PENDING', q=q)}}" class="btn-admin" style="background:{% if status_filter=='PENDING' %}#d97706{% else %}#334155{% endif %}">⏳ Menunggu</a>
        <a href="{{url_for('pesanan_online_admin', status='PROSES_POS', q=q)}}" class="btn-admin" style="background:{% if status_filter=='PROSES_POS' %}#16a34a{% else %}#334155{% endif %}">✅ Masuk POS</a>
        <a href="{{url_for('pesanan_online_admin', status='BATAL', q=q)}}" class="btn-admin" style="background:{% if status_filter=='BATAL' %}#dc2626{% else %}#334155{% endif %}">❌ Batal</a>
      </div>
    </div>

    <div class="card" style="margin-bottom:16px;padding:12px 16px">
      <form method="get" class="filters" style="display:flex;gap:10px;align-items:center;flex-wrap:wrap">
        <input type="hidden" name="status" value="{{status_filter}}">
        <label style="flex:1;min-width:220px;font-size:12.5px;font-weight:700;color:var(--ff-muted)">
          🔍 Cari Pesanan (No. Order / Nama / HP / Produk):
          <input type="text" name="q" value="{{q}}" placeholder="Ketik kata kunci pencarian..." style="width:100%;margin-top:6px;padding:8px 12px;border:1px solid #cbd5e1;border-radius:6px">
        </label>
        <button type="submit" class="btn-admin" style="align-self:flex-end">Cari</button>
        {% if q %}
          <a href="{{url_for('pesanan_online_admin', status=status_filter)}}" class="btn-admin" style="background:#64748b;align-self:flex-end">✕ Reset</a>
        {% endif %}
      </form>
    </div>

    {% with messages = get_flashed_messages(with_categories=true) %}
      {% if messages %}
        {% for cat, msg in messages %}
          <div style="background:{% if cat=='error' %}#ef4444{% else %}#16a34a{% endif %};color:white;padding:10px 16px;border-radius:6px;margin-bottom:16px;font-weight:700">{{msg}}</div>
        {% endfor %}
      {% endif %}
    {% endwith %}

    <div class="card table-wrap">
      <table>
        <tr>
          <th>No. Order</th>
          <th>Tanggal</th>
          <th>Pembeli / WhatsApp</th>
          <th>Pengiriman</th>
          <th>Produk Dipesan</th>
          <th>Total</th>
          <th>Status</th>
          <th>Aksi</th>
        </tr>
        {% for o in rows %}
        <tr>
          <td style="font-weight:700;font-size:13px">{{o.order_number}}</td>
          <td>{{o.tanggal.strftime('%d/%m/%Y %H:%M') if o.tanggal else '-'}}</td>
          <td>
            <strong>{{o.nama_pembeli}}</strong><br>
            <a href="https://wa.me/{{o.clean_phone}}?text=Halo%20{{o.nama_pembeli}},%20pesanan%20Anda%20di%20DN%20Sport%20({{o.order_number}})%20sedang%20kami%20proses." target="_blank" style="color:#22c55e;font-weight:700">💬 {{o.no_hp}}</a>
          </td>
          <td>
            <small><b>{{o.metode_pengiriman}}</b></small>
            {% if o.alamat %}<br><span style="color:#64748b;font-size:12px">{{o.alamat}}</span>{% endif %}
            {% if o.catatan %}<br><span style="color:#d97706;font-size:12px">Catatan: {{o.catatan}}</span>{% endif %}
          </td>
          <td>
            <ul style="padding-left:14px;margin:0;font-size:12.5px">
              {% for it in o.produk_list %}
                <li>{{it.name}} ({{it.barcode}}) &times; <b>{{it.qty}}</b></li>
              {% endfor %}
            </ul>
          </td>
          <td class="num">{{o.total_harga|rupiah}}</td>
          <td>
            {% if o.status == 'PENDING' %}
              <span style="background:#fef3c7;color:#b45309;padding:4px 8px;border-radius:4px;font-weight:800;font-size:11px">MENUNGGU</span>
            {% elif o.status == 'PROSES_POS' %}
              <span style="background:#dcfce7;color:#15803d;padding:4px 8px;border-radius:4px;font-weight:800;font-size:11px">DIPROSES POS</span>
            {% elif o.status == 'BATAL' %}
              <span style="background:#fee2e2;color:#b91c1c;padding:4px 8px;border-radius:4px;font-weight:800;font-size:11px">DIBATALKAN</span>
            {% else %}
              <span style="background:#e2e8f0;color:#334155;padding:4px 8px;border-radius:4px;font-weight:800;font-size:11px">{{o.status}}</span>
            {% endif %}
          </td>
          <td style="white-space:nowrap">
            {% if o.status == 'PENDING' %}
              <form method="post" action="/pesanan-online/{{o.id}}/proses" style="display:inline">
                <button class="btn-admin" style="background:#16a34a;padding:5px 10px;font-size:12px" title="Lanjutkan ke Kasir POS">🟢 Ke POS</button>
              </form>
              <form method="post" action="/pesanan-online/{{o.id}}/batal" style="display:inline" onsubmit="return confirm('Yakin batalkan pesanan ini?')">
                <button class="btn-admin" style="background:#dc2626;padding:5px 10px;font-size:12px" title="Batalkan Pesanan">❌ Batal</button>
              </form>
            {% else %}
              <small style="color:#64748b">-</small>
            {% endif %}
          </td>
        </tr>
        {% else %}
        <tr><td colspan="8" style="text-align:center;padding:24px;color:#94a3b8">Tidak ada pesanan online.</td></tr>
        {% endfor %}
      </table>
    </div>
    </main></body></html>"""
    return render_template_string(tpl, title="Pesanan Online", rows=rows, status_filter=status_filter)


@app.route("/pesanan-online/<int:order_id>/proses", methods=["POST"])
@login_required
def pesanan_online_proses(order_id):
    db = db_connect()
    try:
        cur = db.cursor(dictionary=True)
        cur.execute("SELECT * FROM pesanan_online WHERE id = %s", (order_id,))
        order = cur.fetchone()
        if not order:
            flash("Pesanan tidak ditemukan.", "error")
            return redirect(url_for("pesanan_online_admin"))

        items = []
        try:
            items = json.loads(order["items_json"])
        except:
            pass

        cart_items = []
        for it in items:
            bc = str(it.get("barcode", "")).strip()
            nm = str(it.get("name", "")).strip()
            qty = int(it.get("qty", 1))
            price = int(it.get("price", 0))
            cart_items.append({
                "barcode": bc,
                "nama": nm,
                "qty": qty,
                "harga_asli": price,
                "diskon": 0,
                "diskon1": 0,
                "diskon2": 0,
                "total": price * qty,
                "is_gratis": False
            })

        # Save to pos_draft so POS cashier loads it
        draft_payload = json.dumps({"cart": cart_items, "payments": {"NON TUNAI": "", "UANG TUNAI": ""}}, ensure_ascii=False)
        cur.execute("""
            INSERT INTO pos_draft (id, payload) VALUES (1, %s)
            ON DUPLICATE KEY UPDATE payload = VALUES(payload)
        """, (draft_payload,))

        # Update order status
        cur.execute("""
            UPDATE pesanan_online
            SET status = 'PROSES_POS', waktu_proses = NOW(), diproses_oleh = %s
            WHERE id = %s
        """, (session.get("admin_user", "ADMIN_WEB"), order_id))

        db.commit()
        flash(f"Pesanan {order['order_number']} berhasil dimuat ke Kasir POS!", "success")
    except Exception as e:
        db.rollback()
        flash(f"Gagal memproses pesanan: {str(e)}", "error")
    finally:
        db.close()

    return redirect(url_for("pesanan_online_admin"))


@app.route("/pesanan-online/<int:order_id>/batal", methods=["POST"])
@login_required
def pesanan_online_batal(order_id):
    db = db_connect()
    try:
        cur = db.cursor()
        cur.execute("""
            UPDATE pesanan_online
            SET status = 'BATAL', waktu_proses = NOW(), diproses_oleh = %s
            WHERE id = %s
        """, (session.get("admin_user", "ADMIN_WEB"), order_id))
        db.commit()
        flash(f"Pesanan #{order_id} telah dibatalkan.", "success")
    except Exception as e:
        db.rollback()
        flash(f"Gagal membatalkan pesanan: {str(e)}", "error")
    finally:
        db.close()

    return redirect(url_for("pesanan_online_admin"))


if __name__ == "__main__":
    from waitress import serve
    print("[INFO] DN Sport Web & Katalog berjalan di http://127.0.0.1:8080")
    serve(app, host="127.0.0.1", port=8080, threads=8)
