"""
Build a standalone CM Action — Past Promotions dashboard.

Reads our ERP data, finds past monthly promo campaigns, computes uplift /
revenue / RUC per SKU per campaign, and writes a single self-contained
HTML file matching the dark Polleo CM Action preview design.

The output (`cm_action_history.html`) opens in any browser — no server,
no Streamlit. Just double-click. Re-run this script weekly to refresh.

Run:
    python cm_action_history_build.py
"""
from __future__ import annotations

import json
from datetime import datetime
from pathlib import Path

import pandas as pd

ROOT = Path(__file__).parent
DATA = ROOT / "data"
OUT  = ROOT / "cm_action_history.html"

# ---------- Load data ----------
def _load(name: str) -> pd.DataFrame:
    p = DATA / name
    if not p.exists():
        return pd.DataFrame()
    df = pd.read_csv(p)
    df.columns = [str(c).strip().lower() for c in df.columns]
    return df

erp     = _load("erp_promo_calendar.csv")
sales   = _load("sales_clean.csv")
plan    = _load("sku_plan_list.csv")
prices  = _load("sku_prices.csv")
costs   = _load("sku_costs.csv")

if erp.empty or sales.empty:
    raise SystemExit("Missing erp_promo_calendar.csv or sales_clean.csv in data/. Cannot build.")

# Past only
iso = datetime.now().isocalendar()
cur_y, cur_w = int(iso[0]), int(iso[1])
erp_past = erp[(erp["year"].astype(int) * 100 + erp["week"].astype(int))
                 < (cur_y * 100 + cur_w)].copy()

# Promo type — explode comma-separated; primary = first label
erp_past["primary_type"] = erp_past["promo_types"].astype(str).str.split(";").str[0].str.strip()

# ---------- Identify campaigns ----------
# A campaign = (primary_type, year). For each campaign, collect SKUs and
# the contiguous week range where promo activity was observed.
campaigns = []
for (ptype, yr), g in erp_past.groupby(["primary_type", "year"]):
    weeks = sorted(g["week"].astype(int).unique().tolist())
    if not weeks:
        continue
    # If gap > 6 weeks, split into separate campaigns
    blocks = [[weeks[0]]]
    for w in weeks[1:]:
        if w - blocks[-1][-1] <= 6:
            blocks[-1].append(w)
        else:
            blocks.append([w])
    for blk in blocks:
        first_w, last_w = blk[0], blk[-1]
        duration = last_w - first_w + 1
        # MONTHLY cadence filter: duration >= 4 weeks OR campaigns whose week
        # spread is at least 4 weeks (i.e. crosses a month boundary).
        if duration < 4:
            continue
        skus = sorted(g[g["week"].astype(int).isin(blk)]["sku"].unique().tolist())
        campaigns.append({
            "type": ptype, "year": int(yr),
            "start_week": first_w, "end_week": last_w,
            "duration": duration, "skus": skus,
        })

# Sort: most recent first
campaigns.sort(key=lambda c: (-c["year"], -c["end_week"]))


# ---------- Compute metrics per campaign ----------
sales["yw"] = sales["year"].astype(int) * 100 + sales["week"].astype(int)
prices_map  = dict(zip(prices["sku"], prices.get("avg_sell_price", 0))) if "sku" in prices.columns else {}
ruc_per_unit_map = {}
if not costs.empty and "sku" in costs.columns and "ruc" in costs.columns:
    ruc_per_unit_map = dict(zip(costs["sku"], costs["ruc"]))

name_map = dict(zip(plan.get("sku", []), plan.get("name", []))) if not plan.empty else {}
cat_map  = dict(zip(plan.get("sku", []), plan.get("cat", "")))  if not plan.empty else {}
ozn_map  = dict(zip(plan.get("sku", []), plan.get("oznaka", ""))) if not plan.empty else {}

CAMPAIGN_DATA = []
for c in campaigns:
    yr = c["year"]; sw = c["start_week"]; ew = c["end_week"]
    promo_yws = {yr * 100 + w for w in range(sw, ew + 1)}
    # Base = the 4 weeks preceding the campaign in the same year
    base_yws = {yr * 100 + w for w in range(max(1, sw - 4), sw)}

    sub_during = sales[sales["sku"].isin(c["skus"]) & sales["yw"].isin(promo_yws)]
    sub_base   = sales[sales["sku"].isin(c["skus"]) & sales["yw"].isin(base_yws)]

    # Per SKU stats
    sku_rows = []
    for sku in c["skus"]:
        d_rows = sub_during[sub_during["sku"] == sku]
        b_rows = sub_base[sub_base["sku"] == sku]
        if len(d_rows) == 0:
            continue
        d_qty  = float(d_rows["qty_total"].sum())
        d_n    = max(1, d_rows["yw"].nunique())
        b_qty  = float(b_rows["qty_total"].sum())
        b_n    = max(1, b_rows["yw"].nunique()) if len(b_rows) else 1
        avg_d  = d_qty / d_n
        avg_b  = b_qty / b_n if b_qty > 0 else 0
        uplift = (avg_d / avg_b) if avg_b > 0 else 0
        price  = float(prices_map.get(sku, 0) or 0)
        ruc_pu = float(ruc_per_unit_map.get(sku, 0) or 0)
        rev    = d_qty * price
        ruc    = d_qty * ruc_pu
        sku_rows.append({
            "sku": sku,
            "name": (name_map.get(sku, "") or "")[:60],
            "cat":  cat_map.get(sku, "") or "",
            "tier": ozn_map.get(sku, "") or "",
            "weeks": int(d_rows["yw"].nunique()),
            "qty": int(d_qty),
            "avg_base": round(avg_b, 1),
            "avg_promo": round(avg_d, 1),
            "uplift": round(uplift, 2),
            "rev": round(rev),
            "ruc": round(ruc),
        })
    if not sku_rows:
        continue
    sku_rows.sort(key=lambda r: r["qty"], reverse=True)

    total_qty = sum(r["qty"] for r in sku_rows)
    total_rev = sum(r["rev"] for r in sku_rows)
    total_ruc = sum(r["ruc"] for r in sku_rows)
    avg_uplift = round(sum(r["uplift"] for r in sku_rows if r["uplift"] > 0) /
                        max(1, sum(1 for r in sku_rows if r["uplift"] > 0)), 2)

    CAMPAIGN_DATA.append({
        "id": f"{yr}-{sw:02d}-{c['type'][:30]}",
        "type": c["type"],
        "year": yr,
        "start_week": sw,
        "end_week": ew,
        "duration": c["duration"],
        "n_skus": len(sku_rows),
        "total_qty": total_qty,
        "total_rev": total_rev,
        "total_ruc": total_ruc,
        "avg_uplift": avg_uplift,
        "skus": sku_rows,
    })

# ---------- Build HTML ----------
print(f"Built {len(CAMPAIGN_DATA)} campaigns. Writing HTML...")

generated = datetime.now().strftime("%Y-%m-%d %H:%M")

HTML = """<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\">
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
<title>CM Action — Past Promotions</title>
<link href=\"https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap\" rel=\"stylesheet\">
<style>
:root {
  --bg: #0F1117; --bg-card: #1A1D27; --bg-card-hover: #21242F;
  --bg-sidebar: #14161E; --border: #2A2D3A; --border-light: #353849;
  --text: #E8E9ED; --text-dim: #8B8FA3; --text-muted: #5D6175;
  --accent: #6C63FF; --accent-dim: rgba(108,99,255,0.15);
  --red: #FF6B6B; --red-dim: rgba(255,107,107,0.12);
  --green: #4ADE80; --green-dim: rgba(74,222,128,0.12);
  --amber: #FBBF24; --amber-dim: rgba(251,191,36,0.12);
  --blue: #60A5FA; --orange: #F97316;
  --font: 'DM Sans', -apple-system, sans-serif; --mono: 'JetBrains Mono', monospace;
  --radius: 10px; --radius-sm: 6px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: var(--font); background: var(--bg); color: var(--text); display: flex; min-height: 100vh; font-size: 13px; line-height: 1.5; }
::-webkit-scrollbar { width: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-light); border-radius: 10px; }

.sidebar { width: 320px; min-width: 320px; background: var(--bg-sidebar); border-right: 1px solid var(--border); padding: 20px 16px; overflow-y: auto; height: 100vh; position: sticky; top: 0; }
.sidebar h2 { font-size: 15px; font-weight: 600; margin-bottom: 4px; display: flex; align-items: center; gap: 8px; }
.sidebar .sub { font-size: 11px; color: var(--text-muted); margin-bottom: 16px; }
.sb-section { margin-bottom: 16px; }
.sb-label { font-size: 10px; font-weight: 500; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; }
.sb-input, .sb-select { width: 100%; padding: 8px 10px; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-sm); color: var(--text); font-family: var(--font); font-size: 12px; outline: none; }
.sb-input:focus, .sb-select:focus { border-color: var(--accent); }

.campaign-list { display: flex; flex-direction: column; gap: 6px; }
.camp-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 12px; cursor: pointer; transition: all 0.15s; }
.camp-card:hover { border-color: var(--accent); background: var(--bg-card-hover); }
.camp-card.active { border-color: var(--accent); background: var(--accent-dim); }
.camp-name { font-size: 12px; font-weight: 600; }
.camp-meta { font-size: 10px; color: var(--text-dim); margin-top: 3px; font-family: var(--mono); }
.camp-stats { display: flex; gap: 10px; margin-top: 6px; font-size: 10px; }
.camp-stats span { color: var(--text-dim); }
.camp-stats b { color: var(--text); }

.main { flex: 1; padding: 24px 28px; overflow-y: auto; min-width: 0; }

.page-header { background: linear-gradient(135deg, #1B1440 0%, #2D1B69 50%, #1A1145 100%); padding: 22px 28px; border-radius: var(--radius); margin-bottom: 22px; position: relative; overflow: hidden; }
.page-header::before { content: ''; position: absolute; top: -50%; right: -10%; width: 300px; height: 300px; background: radial-gradient(circle, rgba(108,99,255,0.15) 0%, transparent 60%); }
.page-header h1 { font-size: 22px; font-weight: 600; color: white; position: relative; }
.page-header p { font-size: 12px; color: #8B7FC7; margin-top: 4px; position: relative; }

.kpi-row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; margin-bottom: 20px; }
.kpi-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 14px 16px; }
.kpi-card.accent { border-left: 3px solid var(--accent); }
.kpi-card.green { border-left: 3px solid var(--green); }
.kpi-card.amber { border-left: 3px solid var(--amber); }
.kpi-card.blue { border-left: 3px solid var(--blue); }
.kpi-card.red { border-left: 3px solid var(--red); }
.kpi-val { font-size: 20px; font-weight: 600; }
.kpi-lbl { font-size: 10px; color: var(--text-dim); margin-top: 4px; text-transform: uppercase; letter-spacing: 0.4px; }

.section-title { font-size: 14px; font-weight: 600; margin: 24px 0 12px; display: flex; align-items: center; gap: 8px; }

.sku-table { width: 100%; border-collapse: collapse; font-size: 11px; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.sku-table th { text-align: left; padding: 10px 12px; color: var(--text-dim); font-weight: 500; border-bottom: 1px solid var(--border); font-size: 10px; text-transform: uppercase; letter-spacing: 0.4px; background: rgba(0,0,0,0.18); }
.sku-table td { padding: 9px 12px; border-bottom: 1px solid var(--border); font-family: var(--mono); }
.sku-table tr:last-child td { border-bottom: none; }
.sku-table tr:hover { background: var(--bg-card-hover); }
.sku-table .sku-code { color: var(--accent); font-weight: 500; }
.sku-table .name { font-family: var(--font); color: var(--text); }
.sku-table .num { text-align: right; }
.sku-table .uplift-good { color: var(--green); font-weight: 600; }
.sku-table .uplift-ok   { color: var(--amber); font-weight: 600; }
.sku-table .uplift-bad  { color: var(--red); font-weight: 600; }
.tier-pill { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 9px; font-weight: 600; font-family: var(--font); }
.tier-gold   { background: var(--amber-dim); color: var(--amber); }
.tier-silver { background: rgba(160,160,180,0.15); color: #B5B7C8; }
.tier-bronze { background: rgba(205,127,50,0.15); color: #D89E73; }

.empty-state { padding: 60px 20px; text-align: center; color: var(--text-dim); }
.empty-state .icon { font-size: 48px; margin-bottom: 14px; }

.search-input { width: 100%; padding: 9px 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-sm); color: var(--text); font-family: var(--font); font-size: 12px; outline: none; margin-bottom: 12px; }
.search-input:focus { border-color: var(--accent); }

.export-row { margin-top: 18px; display: flex; gap: 10px; }
.btn { padding: 9px 14px; border: none; border-radius: var(--radius-sm); font-family: var(--font); font-size: 12px; font-weight: 500; cursor: pointer; transition: all 0.15s; }
.btn-ghost { background: transparent; border: 1px solid var(--border); color: var(--text-dim); }
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }

@media (max-width: 1100px) {
  .kpi-row { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 900px) {
  body { flex-direction: column; }
  .sidebar { width: 100%; min-width: unset; height: auto; position: relative; }
  .kpi-row { grid-template-columns: repeat(2, 1fr); }
}
</style>
</head>
<body>

<div class=\"sidebar\">
  <h2>📜 CM Action — Past</h2>
  <div class=\"sub\">Generated __GENERATED__ · CW__CURW__/__CURY__</div>

  <div class=\"sb-section\">
    <div class=\"sb-label\">Search campaign</div>
    <input class=\"sb-input\" id=\"search\" placeholder=\"e.g. Black Friday\" oninput=\"render()\">
  </div>

  <div class=\"sb-section\">
    <div class=\"sb-label\">Year</div>
    <select class=\"sb-select\" id=\"yearFilter\" onchange=\"render()\">
      <option value=\"all\">All years</option>
    </select>
  </div>

  <div class=\"sb-section\">
    <div class=\"sb-label\">Min duration (weeks)</div>
    <select class=\"sb-select\" id=\"durFilter\" onchange=\"render()\">
      <option value=\"4\" selected>4+ (monthly)</option>
      <option value=\"6\">6+</option>
      <option value=\"8\">8+ (1.5 month)</option>
      <option value=\"12\">12+ (quarterly)</option>
    </select>
  </div>

  <div class=\"sb-section\">
    <div class=\"sb-label\" id=\"campCount\">Campaigns</div>
    <div class=\"campaign-list\" id=\"campList\"></div>
  </div>
</div>

<div class=\"main\">
  <div class=\"page-header\">
    <h1>📜 CM Action — Past Promotions</h1>
    <p>Past monthly+ campaigns from the ERP promo calendar · uplift, units, revenue, RUC per SKU</p>
  </div>

  <div id=\"detail\">
    <div class=\"empty-state\">
      <div class=\"icon\">👈</div>
      <div>Pick a campaign on the left to see SKU-level performance.</div>
    </div>
  </div>
</div>

<script>
const DATA = __DATA_JSON__;
let activeId = null;

function tierClass(t) {
  if (!t) return '';
  if (t.indexOf('GOLD')   !== -1) return 'tier-gold';
  if (t.indexOf('SILVER') !== -1) return 'tier-silver';
  if (t.indexOf('BRONZE') !== -1) return 'tier-bronze';
  return '';
}
function fmt(n) { return n.toLocaleString('en-US'); }
function fmt0(n) { return Math.round(n).toLocaleString('en-US'); }

function upliftClass(u) {
  if (u >= 1.5) return 'uplift-good';
  if (u >= 1.15) return 'uplift-ok';
  return 'uplift-bad';
}

function renderCampaignList() {
  const search   = document.getElementById('search').value.toLowerCase();
  const yearF    = document.getElementById('yearFilter').value;
  const minDur   = parseInt(document.getElementById('durFilter').value, 10);

  const filtered = DATA.filter(c => {
    if (yearF !== 'all' && c.year !== parseInt(yearF, 10)) return false;
    if (c.duration < minDur) return false;
    if (search && c.type.toLowerCase().indexOf(search) === -1) return false;
    return true;
  });

  document.getElementById('campCount').textContent = `Campaigns (${filtered.length})`;
  const list = document.getElementById('campList');
  list.innerHTML = '';
  filtered.forEach(c => {
    const div = document.createElement('div');
    div.className = 'camp-card' + (c.id === activeId ? ' active' : '');
    div.innerHTML = `
      <div class=\"camp-name\">${c.type}</div>
      <div class=\"camp-meta\">CW${c.start_week}–CW${c.end_week} (${c.year}) · ${c.duration}w</div>
      <div class=\"camp-stats\"><span>${c.n_skus} SKUs</span><span><b>${fmt(c.total_qty)}</b> units</span><span>${c.avg_uplift}× upl</span></div>
    `;
    div.onclick = () => { activeId = c.id; render(); };
    list.appendChild(div);
  });

  if (!activeId && filtered.length) {
    activeId = filtered[0].id;
    renderDetail();
  }
}

function renderDetail() {
  const c = DATA.find(x => x.id === activeId);
  const detail = document.getElementById('detail');
  if (!c) {
    detail.innerHTML = '<div class=\"empty-state\"><div class=\"icon\">🔍</div><div>No campaign selected.</div></div>';
    return;
  }

  let skuRowsHtml = '';
  c.skus.forEach(s => {
    const upClass = s.uplift > 0 ? upliftClass(s.uplift) : '';
    skuRowsHtml += `
      <tr>
        <td class=\"sku-code\">${s.sku}</td>
        <td class=\"name\">${s.name}</td>
        <td class=\"name\">${s.cat}</td>
        <td><span class=\"tier-pill ${tierClass(s.tier)}\">${s.tier || '—'}</span></td>
        <td class=\"num\">${fmt(s.weeks)}</td>
        <td class=\"num\">${fmt(s.qty)}</td>
        <td class=\"num\">${fmt(s.avg_base)}</td>
        <td class=\"num\">${fmt(s.avg_promo)}</td>
        <td class=\"num ${upClass}\">${s.uplift > 0 ? s.uplift + '×' : '—'}</td>
        <td class=\"num\">€${fmt0(s.rev)}</td>
        <td class=\"num\">€${fmt0(s.ruc)}</td>
      </tr>
    `;
  });

  detail.innerHTML = `
    <div class=\"section-title\">📌 ${c.type} — ${c.year}</div>
    <div style=\"font-size:11px;color:var(--text-dim);margin-bottom:18px;font-family:var(--mono);\">
      CW${c.start_week} → CW${c.end_week} · ${c.duration} weeks · ${c.n_skus} SKUs
    </div>

    <div class=\"kpi-row\">
      <div class=\"kpi-card accent\"><div class=\"kpi-val\">${fmt(c.n_skus)}</div><div class=\"kpi-lbl\">SKUs</div></div>
      <div class=\"kpi-card blue\"><div class=\"kpi-val\">${fmt(c.total_qty)}</div><div class=\"kpi-lbl\">Total units</div></div>
      <div class=\"kpi-card green\"><div class=\"kpi-val\">€${fmt0(c.total_rev)}</div><div class=\"kpi-lbl\">Revenue</div></div>
      <div class=\"kpi-card amber\"><div class=\"kpi-val\">€${fmt0(c.total_ruc)}</div><div class=\"kpi-lbl\">RUC</div></div>
      <div class=\"kpi-card ${c.avg_uplift >= 1.5 ? 'green' : (c.avg_uplift >= 1.15 ? 'amber' : 'red')}\">
        <div class=\"kpi-val\">${c.avg_uplift}×</div><div class=\"kpi-lbl\">Avg uplift</div>
      </div>
    </div>

    <div class=\"section-title\">📦 SKUs in this campaign</div>
    <table class=\"sku-table\">
      <tr>
        <th>SKU</th><th>Name</th><th>Category</th><th>Tier</th>
        <th class=\"num\">Weeks</th><th class=\"num\">Units</th>
        <th class=\"num\">Avg base/wk</th><th class=\"num\">Avg promo/wk</th>
        <th class=\"num\">Uplift</th><th class=\"num\">Revenue</th><th class=\"num\">RUC</th>
      </tr>
      ${skuRowsHtml}
    </table>

    <div class=\"export-row\">
      <button class=\"btn btn-ghost\" onclick=\"exportCsv()\">⬇️ Export this campaign as CSV</button>
    </div>
  `;
}

function exportCsv() {
  const c = DATA.find(x => x.id === activeId);
  if (!c) return;
  const head = ['sku','name','category','tier','weeks','units','avg_base','avg_promo','uplift','revenue_eur','ruc_eur'];
  const rows = c.skus.map(s => [
    s.sku, s.name.replaceAll(',', ' '), s.cat, s.tier, s.weeks, s.qty,
    s.avg_base, s.avg_promo, s.uplift, s.rev, s.ruc,
  ].join(','));
  const csv = head.join(',') + '\\n' + rows.join('\\n');
  const blob = new Blob([csv], {type: 'text/csv'});
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = `campaign_${c.id.replaceAll('/', '-')}.csv`;
  a.click();
}

function render() { renderCampaignList(); renderDetail(); }

// Populate year filter
const years = Array.from(new Set(DATA.map(c => c.year))).sort((a,b)=>b-a);
const yf = document.getElementById('yearFilter');
years.forEach(y => { const o=document.createElement('option'); o.value=y; o.textContent=y; yf.appendChild(o); });

render();
</script>
</body>
</html>
"""

html = (HTML
        .replace("__GENERATED__", generated)
        .replace("__CURW__", str(cur_w))
        .replace("__CURY__", str(cur_y))
        .replace("__DATA_JSON__", json.dumps(CAMPAIGN_DATA, ensure_ascii=False)))

OUT.write_text(html, encoding="utf-8")

print(f"Wrote: {OUT}")
print(f"  Campaigns: {len(CAMPAIGN_DATA)}")
print(f"  File size: {OUT.stat().st_size // 1024} KB")
print()
print("Open the file directly in your browser:")
print(f"  file:///{OUT.as_posix()}")
