// ========================================================
//  Mock data for the Easytull / Easytoll dashboard
// ========================================================

const MONTHS_SV = [
  "Januari","Februari","Mars","April","Maj","Juni",
  "Juli","Augusti","September","Oktober","November","December"
];
const VECKODAGAR = ["Mån","Tis","Ons","Tor","Fre","Lör","Sön"];
const VECKODAGAR_FULL = ["Måndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag","Söndag"];

const TJANSTER = ["Import","Export","Transit","HS-kod","Ursprungsintyg","Rådgivning"];
const STATUSAR = ["Konverterad","Informationsförfrågan","Avböjde pris","Ej svarat","Pågående"];
const KONTAKTSATT = ["E-post","Telefon","Formulär"];

const KANALER_SE = ["Google Ads SE","Organisk SE","Direkt"];
const KANALER_NO = ["Google Ads NO","Organisk NO","Direkt"];

// deterministic pseudo-random so the dashboard is stable between reloads
function mulberry32(a){
  return function(){
    a |= 0; a = a + 0x6D2B79F5 | 0;
    let t = Math.imul(a ^ a >>> 15, 1 | a);
    t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  }
}
function pick(rng, arr, weights){
  if(!weights){ return arr[Math.floor(rng()*arr.length)]; }
  const total = weights.reduce((a,b)=>a+b,0);
  let r = rng()*total;
  for(let i=0;i<arr.length;i++){ r -= weights[i]; if(r<=0) return arr[i]; }
  return arr[arr.length-1];
}

// Daysinmonth helper
function daysInMonth(year, monthIdx){ return new Date(year, monthIdx+1, 0).getDate(); }

// Pineberry tracking — keyword / landing page / campaign pools
const SOKORD_BY_TJANST = {
  SE: {
    "Import":         ["tullombud import", "importera från kina", "import sverige tull", "tullhantering import", "importtjänst"],
    "Export":         ["export tullhantering", "exportdeklaration", "export till storbritannien", "exporttullbehandling"],
    "Transit":        ["transit tull", "T1 transitering", "tullombud transit"],
    "HS-kod":         ["hs-kod sökning", "tulltariff", "varukod export", "hs kod kläder"],
    "Ursprungsintyg": ["ursprungsintyg eur1", "EUR.1 certifikat", "form A ursprung"],
    "Rådgivning":     ["tullrådgivning", "tullkonsult", "tullexpert företag"],
  },
  NO: {
    "Import":         ["tollklarering import", "import til norge", "tollbehandling import", "importere fra kina"],
    "Export":         ["eksport tollklarering", "eksportdeklarasjon", "eksport til EU"],
    "Transit":        ["transittering toll", "T1 transitt"],
    "HS-kod":         ["hs-kode søk", "tolltariff norge", "varekode"],
    "Ursprungsintyg": ["opprinnelsesbevis EUR1", "EUR.1 sertifikat"],
    "Rådgivning":     ["tollrådgivning", "tollkonsulent"],
  }
};
const LANDNING_BY_TJANST = {
  SE: {
    "Import":         ["/tjanster/import", "/import-tullhantering", "/"],
    "Export":         ["/tjanster/export", "/export-tullhantering"],
    "Transit":        ["/tjanster/transit"],
    "HS-kod":         ["/tjanster/hs-kod", "/verktyg/hs-sok"],
    "Ursprungsintyg": ["/tjanster/ursprungsintyg"],
    "Rådgivning":     ["/radgivning", "/kontakt"],
  },
  NO: {
    "Import":         ["/tjenester/import", "/toll-import", "/"],
    "Export":         ["/tjenester/eksport"],
    "Transit":        ["/tjenester/transitt"],
    "HS-kod":         ["/tjenester/hs-kode", "/verktoy/hs-sok"],
    "Ursprungsintyg": ["/tjenester/opprinnelsesbevis"],
    "Rådgivning":     ["/radgivning", "/kontakt"],
  }
};
const KAMPANJ_BY_KANAL = {
  "Google Ads SE": ["Tullombud Import · SE", "Export tullhantering · SE", "HS-kod sök · SE", "Brand · easytull.se", "Rådgivning · SE"],
  "Google Ads NO": ["Tollklarering Import · NO", "Eksport · NO", "HS-kode · NO", "Brand · easytoll.no"],
};

// =====================================================================
//  Tracking / attribution helpers (Fluent Forms + UTM + Google Ads ids)
// =====================================================================

const DOMAIN_BY_MARKET = { SE: "https://easytull.se", NO: "https://easytoll.no" };

const GAD_CAMPAIGN_IDS = ["21043778921","19872451100","20114339887","20881104442"];
const SOCIAL_REFERRERS = ["https://l.facebook.com/","https://www.linkedin.com/","https://t.co/"];

function shortId(rng, n=10){
  const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
  let out = "";
  for(let i=0;i<n;i++) out += chars[Math.floor(rng()*chars.length)];
  return out;
}

// Build tracking fields given an "intended" channel. Models real-world
// partial tracking — most paid leads will have *some* attribution, but
// only ~40% will be fully tagged with gclid/UTM. Many organic and direct
// leads will have nothing beyond the form page.
function buildTracking(rng, kanal, market, tjanst, landning, kampanj, sokord){
  const domain = DOMAIN_BY_MARKET[market];
  const tracking = {
    source_url:        null,
    first_landing_page:null,
    form_page:         null,
    original_referrer: null,
    utm_source:        null,
    utm_medium:        null,
    utm_campaign:      null,
    utm_term:          null,
    utm_content:       null,
    gclid:             null,
    gbraid:            null,
    wbraid:            null,
    gad_source:        null,
    gad_campaignid:    null,
    traffic_source:    null,
    traffic_medium:    null,
  };

  // How often does the user submit from the same page they landed on?
  const submittedHere = rng() < 0.45; // 45 % stay; 55 % click around then submit from /kontakt/
  const formPage = submittedHere ? landning : (market === "SE" ? "/kontakt" : "/kontakt");
  tracking.form_page = formPage;

  if(kanal.startsWith("Google Ads")){
    // Most paid clicks have gclid OR gbraid (newer wbraid for cross-device).
    // Random subset has full UTM tagging.
    const hasUtm = rng() < 0.55;
    const r = rng();
    if(r < 0.55){ tracking.gclid = "Cj0KCQiA" + shortId(rng, 22); }
    else if(r < 0.85){ tracking.gbraid = "0AAAAA" + shortId(rng, 18); }
    else { tracking.wbraid = "Cn0KCQ" + shortId(rng, 18); }

    tracking.gad_source = "1";
    tracking.gad_campaignid = pick(rng, GAD_CAMPAIGN_IDS);

    if(hasUtm){
      tracking.utm_source   = "google";
      tracking.utm_medium   = pick(rng, ["cpc","ppc","paid"], [0.7,0.2,0.1]);
      tracking.utm_campaign = (kampanj || "").toLowerCase().replace(/\s+/g,"-");
      tracking.utm_term     = sokord;
      tracking.utm_content  = pick(rng, ["headline-a","headline-b","sitelink-1","resp-1"]);
    }

    // Source URL: the page they landed on with full tagging
    const params = new URLSearchParams();
    if(tracking.gclid) params.set("gclid", tracking.gclid);
    if(tracking.gbraid) params.set("gbraid", tracking.gbraid);
    if(tracking.wbraid) params.set("wbraid", tracking.wbraid);
    if(tracking.gad_source) params.set("gad_source", tracking.gad_source);
    if(tracking.gad_campaignid) params.set("gad_campaignid", tracking.gad_campaignid);
    if(hasUtm){
      params.set("utm_source", tracking.utm_source);
      params.set("utm_medium", tracking.utm_medium);
      params.set("utm_campaign", tracking.utm_campaign);
      if(tracking.utm_term) params.set("utm_term", tracking.utm_term);
    }

    tracking.first_landing_page = landning;
    // Source URL: 60 % retain Ads params, 40 % only show /kontakt (lost when navigating)
    const retain = rng() < 0.6;
    tracking.source_url = retain
      ? `${domain}${landning}?${params.toString()}`
      : `${domain}${formPage}`;

    tracking.original_referrer = "https://www.google.com/";
    tracking.traffic_source = "google";
    tracking.traffic_medium = "cpc";
    return tracking;
  }

  if(kanal.startsWith("Organisk")){
    const eng = (kalla => kalla === "Bing" ? "bing" : "google");
    const isBing = rng() < 0.15;
    tracking.first_landing_page = landning;
    tracking.source_url = `${domain}${formPage}`;
    tracking.original_referrer = isBing
      ? "https://www.bing.com/"
      : "https://www.google.com/";
    tracking.traffic_source = isBing ? "bing" : "google";
    tracking.traffic_medium = "organic";
    return tracking;
  }

  // Direct / okänd — about 30 % have no referrer at all, rest may have a referrer
  const r = rng();
  if(r < 0.55){
    // truly direct
    tracking.first_landing_page = landning || "/";
    tracking.source_url = `${domain}${formPage}`;
    tracking.original_referrer = null;
    tracking.traffic_source = "(direct)";
    tracking.traffic_medium = "(none)";
  } else if(r < 0.85){
    // referral
    tracking.first_landing_page = landning;
    tracking.source_url = `${domain}${formPage}`;
    tracking.original_referrer = pick(rng, [
      "https://www.tullverket.se/",
      "https://www.linkedin.com/feed/",
      "https://www.regeringen.se/",
      "https://www.svt.se/",
    ]);
    tracking.traffic_source = (new URL(tracking.original_referrer)).hostname.replace("www.","");
    tracking.traffic_medium = "referral";
  } else {
    // social
    tracking.first_landing_page = landning;
    tracking.source_url = `${domain}${formPage}`;
    tracking.original_referrer = pick(rng, SOCIAL_REFERRERS);
    tracking.traffic_source = "social";
    tracking.traffic_medium = "social";
  }
  return tracking;
}

// Classify a tracking object into a Källa + Säkerhet badge.
function classifyAttribution(t){
  const hasAds = !!(t.gclid || t.gbraid || t.wbraid || t.gad_campaignid || t.gad_source);
  const utmPaid = t.utm_medium && /^(cpc|ppc|paid)$/i.test(t.utm_medium);
  const srcHasAds = t.source_url && /(gclid|gbraid|wbraid|gad_source|gad_campaignid)=/i.test(t.source_url);

  if(t.attribution_override){
    return { kalla_kategori: t.attribution_override.kalla, attribution_confidence: t.attribution_override.confidence };
  }

  if(hasAds || utmPaid){
    return { kalla_kategori: "Google Ads", attribution_confidence: "Hög" };
  }
  if(srcHasAds){
    return { kalla_kategori: "Google Ads", attribution_confidence: "Medel" };
  }
  const ref = t.original_referrer || "";
  if(/google\./i.test(ref)){
    return { kalla_kategori: "Google Organic", attribution_confidence: "Medel" };
  }
  if(/bing\./i.test(ref)){
    return { kalla_kategori: "Bing Organic", attribution_confidence: "Medel" };
  }
  if(ref && /(facebook|linkedin|twitter|instagram|t\.co)/i.test(ref)){
    return { kalla_kategori: "Referral", attribution_confidence: "Medel" };
  }
  if(ref){
    return { kalla_kategori: "Referral", attribution_confidence: "Låg" };
  }
  // No referrer, no UTM, no Ads param
  if(t.form_page && /(kontakt)/i.test(t.form_page) && !t.first_landing_page){
    return { kalla_kategori: "Okänd / kontaktsida", attribution_confidence: "Låg" };
  }
  if(!t.source_url && !t.first_landing_page){
    return { kalla_kategori: "Okänd", attribution_confidence: "Okänd" };
  }
  return { kalla_kategori: "Direct / okänd", attribution_confidence: "Låg" };
}

const KALLA_KATEGORIER = [
  "Google Ads","Google Organic","Bing Organic","Direct / okänd","Referral","Manuell","Okänd / kontaktsida","Okänd"
];
const CONFIDENCE_NIVAER = ["Hög","Medel","Låg","Okänd"];

function generateLeads(market, year, monthIdx, seed){
  const rng = mulberry32(seed);
  const dim = daysInMonth(year, monthIdx);
  // Cap "current" month to today so we don't fabricate leads in the future.
  const todayY = _TODAY_FOR_PERIODS.getFullYear();
  const todayM = _TODAY_FOR_PERIODS.getMonth();
  const todayD = _TODAY_FOR_PERIODS.getDate();
  const isCurrentMonth = (year === todayY && monthIdx === todayM);
  const isFutureMonth  = (year > todayY) || (year === todayY && monthIdx > todayM);
  if(isFutureMonth) return [];
  const lastDay = isCurrentMonth ? todayD : dim;
  // approximate target leads volume for the month (SE bigger than NO)
  const baseVolume = market === "SE" ? 38 + Math.floor(rng()*14) : 24 + Math.floor(rng()*10);
  const leads = [];
  let id = 1;
  for(let d=1; d<=lastDay; d++){
    const weekday = new Date(year, monthIdx, d).getDay(); // 0=Sun ... 6=Sat
    // fewer leads on weekends
    const isWeekend = weekday===0 || weekday===6;
    const meanPerDay = (baseVolume/dim) * (isWeekend ? 0.25 : 1.25);
    let count = Math.max(0, Math.round(meanPerDay + (rng()-0.5)*1.6));
    if(rng() < 0.15) count = Math.max(0, count - 1); // some quiet days
    for(let k=0;k<count;k++){
      const hour = 7 + Math.floor(rng()*11); // 7–17
      const minute = Math.floor(rng()*60);
      const kanal = market==="SE"
        ? pick(rng, KANALER_SE, [0.55, 0.30, 0.15])
        : pick(rng, KANALER_NO, [0.55, 0.30, 0.15]);
      const tjanst = pick(rng, TJANSTER, [0.30, 0.22, 0.10, 0.18, 0.10, 0.10]);
      const kontakt = pick(rng, KONTAKTSATT, [0.45, 0.30, 0.25]);
      // status: paid channels convert slightly worse than organic
      const conv = kanal.startsWith("Organisk") ? 0.32 : (kanal==="Direkt" ? 0.40 : 0.24);
      const r = rng();
      let status;
      if(r < conv) status = "Konverterad";
      else if(r < conv + 0.22) status = "Informationsförfrågan";
      else if(r < conv + 0.34) status = "Avböjde pris";
      else if(r < conv + 0.50) status = "Pågående";
      else status = "Ej svarat";

      const ordervarde = status === "Konverterad"
        ? Math.round(2500 + rng()*22000)
        : 0;

      const anteckningar = [
        "", "", "", "", // most empty
        "Skickat offert, väntar svar.",
        "Brådskande sändning från Hamburg, kund vill ha bekräftelse innan fredag och har efterfrågat samlat månadsavtal om volymerna stämmer.",
        "Återkoppla nästa vecka — kunden är på resa.",
        "Pågående offertförhandling, vill ha 5 % rabatt på årsvolym.",
        "Företagskund · återkommande sedan 2023.",
        "Privatperson, mindre engångsärende — sannolikt lågt ordervärde.",
        "Vill ha samlat avtal för både import och export, hänvisat till Anders.",
        "Kund frågade om vi hanterar farligt gods (ADR) — väntar internt besked."
      ];

      // Pineberry fields
      let kalla, kampanj, sokord, landning;
      if(kanal.startsWith("Google Ads")){
        kalla = "Google";
        kampanj = pick(rng, KAMPANJ_BY_KANAL[kanal]);
        sokord  = pick(rng, SOKORD_BY_TJANST[market][tjanst]);
        landning = pick(rng, LANDNING_BY_TJANST[market][tjanst]);
      } else if(kanal.startsWith("Organisk")){
        kalla = rng() < 0.85 ? "Google" : "Bing";
        kampanj = "";
        sokord  = pick(rng, SOKORD_BY_TJANST[market][tjanst]);
        landning = pick(rng, LANDNING_BY_TJANST[market][tjanst]);
      } else {
        kalla = "Direkt";
        kampanj = "";
        sokord = "";
        landning = "/";
      }

      leads.push({
        id: `${market}-${year}-${monthIdx+1}-${id++}`,
        datum: `${year}-${String(monthIdx+1).padStart(2,"0")}-${String(d).padStart(2,"0")}`,
        tid: `${String(hour).padStart(2,"0")}:${String(minute).padStart(2,"0")}`,
        veckodag: VECKODAGAR_FULL[(weekday+6)%7],
        kanal,
        kalla,
        kampanj,
        sokord,
        landning,
        kontakt,
        tjanst,
        land: market,
        status,
        ordervarde,
        anteckning: pick(rng, anteckningar),
        ...buildTracking(rng, kanal, market, tjanst, landning, kampanj, sokord),
      });
    }
  }
  return leads;
}

// 6 months of data, current = April 2026 (index 3) ish — we'll generate Nov 2025 → Apr 2026
// Bygg PERIODS dynamiskt från startdatum till och med "nuvarande" månad.
// Företaget startade Mars 2026. När en ny månad börjar dyker den upp av sig själv.
const START_MONTH = { year: 2026, month: 2 }; // Mars 2026
// Treat "today" deterministically (matches TODAY constant later). Updated 14 maj 2026.
const _TODAY_FOR_PERIODS = new Date(2026, 4, 14);

const PERIODS = (function buildPeriods(){
  const out = [];
  let y = START_MONTH.year, m = START_MONTH.month;
  const endY = _TODAY_FOR_PERIODS.getFullYear();
  const endM = _TODAY_FOR_PERIODS.getMonth();
  while(y < endY || (y === endY && m <= endM)){
    out.push({ year: y, month: m, label: `${MONTHS_SV[m]} ${y}` });
    m++;
    if(m > 11){ m = 0; y++; }
  }
  return out;
})();

const LEADS_DB = {};
const BUDGET_DB = {};

PERIODS.forEach((p, idx) => {
  const seedSE = (p.year*100 + p.month) * 7 + 11;
  const seedNO = (p.year*100 + p.month) * 7 + 23;
  const key = `${p.year}-${p.month}`;
  LEADS_DB[key] = {
    SE: generateLeads("SE", p.year, p.month, seedSE),
    NO: generateLeads("NO", p.year, p.month, seedNO),
  };
  // Run classifier so every lead gets kalla_kategori + attribution_confidence
  ["SE","NO"].forEach(mkt => {
    LEADS_DB[key][mkt].forEach(l => {
      const cls = classifyAttribution(l);
      l.kalla_kategori = cls.kalla_kategori;
      l.attribution_confidence = cls.attribution_confidence;
    });
  });
  // budget grows slightly each month
  const grow = 1 + idx*0.04;
  // Realistic split: Google Ads = variable spend; SEO + Ads management = fixed retainers;
  // setup = one-time costs (excluded from CPL/ROI). Engångs visible only on certain months.
  // PERIODS now starts at Mars 2026 (idx 0 = Mars, 1 = April, 2 = Maj …).
  const setupSE = [];
  const setupNO = [];
  if(idx === 0){
    // Startmånad — onboarding & setup-kostnader
    setupSE.push({ namn:"Pineberry · Onboarding & setup", kostnad: 18000, datum: `${p.year}-${String(p.month+1).padStart(2,"0")}-08` });
    setupNO.push({ namn:"Pineberry · Onboarding NO",       kostnad: 12000, datum: `${p.year}-${String(p.month+1).padStart(2,"0")}-15` });
  }
  if(idx === 1){
    // April — ny landningssida för HS-kod
    setupSE.push({ namn:"Ny landningssida · HS-kod",       kostnad: 8500,  datum: `${p.year}-${String(p.month+1).padStart(2,"0")}-12` });
  }
  if(idx === 2){
    // Maj — GA4 + GTM omkonfigurering
    setupNO.push({ namn:"GA4 + GTM omkonfigurering · NO",  kostnad: 6000,  datum: `${p.year}-${String(p.month+1).padStart(2,"0")}-04` });
  }
  // Anteckningar — växlar per månad (idx 0 = Mars, 1 = April, 2 = Maj)
  const noteSE = idx === 0
    ? "Första aktiva månaden. Lanserat kärnkampanjer för 'tullombud import' och 'export tullhantering'."
    : idx === 1
    ? "Testar nya annonsgrupper för 'tullombud import'. Ny landningssida för HS-kod publicerad 12/4."
    : "Skalat upp budet på toppkampanjer efter starka resultat i april. Justerar bud på svaga sökord.";
  const noteNO = idx === 0
    ? "Onboarding NO klar. Lokal SEO-strategi för Oslo + Bergen påbörjad."
    : idx === 1
    ? "Lokal SEO push for Oslo + Bergen. Justerat annonstexter mot 'tollklarering'."
    : "Ny annonstext på norska – testar 'tollklarering import' som primärt nøkkelord.";
  BUDGET_DB[key] = {
    SE: {
      googleAds:     Math.round(38000*grow),
      seo:           15000,
      adsManagement: 6500,
      ovrigt:        4500,
      setup:         setupSE,
      anteckning:    noteSE,
    },
    NO: {
      googleAds:     Math.round(24000*grow),
      seo:           12000,
      adsManagement: 5000,
      ovrigt:        3200,
      setup:         setupNO,
      anteckning:    noteNO,
    }
  };
});

// ====== Inject 8 realistic scenario leads into April 2026 (SE) ======
(function injectScenarios(){
  const key = "2026-3";
  const bucket = LEADS_DB[key].SE;
  let serial = 0;
  function makeLead(overrides){
    serial++;
    const d = overrides.day || 12;
    const h = overrides.hour || 10;
    const m = overrides.minute || 15;
    const weekday = new Date(2026, 3, d).getDay();
    const base = {
      id: `SE-2026-4-scn-${serial}`,
      datum: `2026-04-${String(d).padStart(2,"0")}`,
      tid: `${String(h).padStart(2,"0")}:${String(m).padStart(2,"0")}`,
      veckodag: VECKODAGAR_FULL[(weekday+6)%7],
      kanal: "Google Ads SE",
      kalla: "Google",
      kampanj: "",
      sokord: "",
      landning: "/tjanster/import",
      kontakt: "Formulär",
      tjanst: "Import",
      land: "SE",
      status: "Pågående",
      ordervarde: 0,
      anteckning: "",
      // tracking defaults — null
      source_url: null, first_landing_page: null, form_page: null,
      original_referrer: null,
      utm_source: null, utm_medium: null, utm_campaign: null, utm_term: null, utm_content: null,
      gclid: null, gbraid: null, wbraid: null,
      gad_source: null, gad_campaignid: null,
      traffic_source: null, traffic_medium: null,
      scenario: overrides.scenario,
    };
    const lead = { ...base, ...overrides };
    const cls = classifyAttribution(lead);
    lead.kalla_kategori = cls.kalla_kategori;
    lead.attribution_confidence = cls.attribution_confidence;
    return lead;
  }

  // 1) Lead from landing page with gbraid + gad_campaignid in Source URL
  bucket.push(makeLead({
    scenario: "1. Komplett Google Ads-spårning",
    day: 18, hour: 9, minute: 12,
    tjanst: "Import", kampanj: "Tullombud Import · SE", sokord: "tullombud import",
    landning: "/tjanster/import", form_page: "/tjanster/import",
    first_landing_page: "/tjanster/import",
    source_url: "https://easytull.se/tjanster/import?gbraid=0AAAAACvE2u9-abc123XYZ&gad_source=1&gad_campaignid=21043778921&utm_source=google&utm_medium=cpc&utm_campaign=tullombud-import-se&utm_term=tullombud+import",
    original_referrer: "https://www.google.com/",
    utm_source: "google", utm_medium: "cpc", utm_campaign: "tullombud-import-se",
    utm_term: "tullombud import", utm_content: "headline-a",
    gbraid: "0AAAAACvE2u9-abc123XYZ", gad_source: "1", gad_campaignid: "21043778921",
    traffic_source: "google", traffic_medium: "cpc",
    status: "Konverterad", ordervarde: 18400,
    anteckning: "Komplett spårning hela vägen. Importföretag i Göteborg, samlat avtal.",
  }));

  // 2) Came via Google Ads but submitted from /kontakt/ — UTM lost on navigation
  bucket.push(makeLead({
    scenario: "2. Ads-trafik men formulär från /kontakt/",
    day: 15, hour: 11, minute: 47,
    tjanst: "Export", kampanj: "Export tullhantering · SE", sokord: "export tullhantering",
    landning: "/tjanster/export",
    first_landing_page: "/tjanster/export",
    form_page: "/kontakt",
    source_url: "https://easytull.se/kontakt", // <— UTM strippad
    original_referrer: "https://www.google.com/",
    // Inga UTM eller gclid i Source URL — bara på första vyn (saknas i Fluent Forms)
    traffic_source: "google", traffic_medium: "cpc",
    status: "Informationsförfrågan", ordervarde: 0,
    anteckning: "Kunden klickade vidare till /kontakt/ innan formulär skickades. UTM tappades — vi vet bara från GA4 att det var paid.",
  }));

  // 3) Pure organic Google
  bucket.push(makeLead({
    scenario: "3. Organisk Google",
    day: 8, hour: 14, minute: 22,
    kanal: "Organisk SE", kampanj: "", sokord: "hs-kod sökning",
    tjanst: "HS-kod", landning: "/tjanster/hs-kod",
    first_landing_page: "/tjanster/hs-kod",
    form_page: "/tjanster/hs-kod",
    source_url: "https://easytull.se/tjanster/hs-kod",
    original_referrer: "https://www.google.com/",
    traffic_source: "google", traffic_medium: "organic",
    kontakt: "E-post",
    status: "Konverterad", ordervarde: 5200,
    anteckning: "Hittade oss via organisk sökning, behövde HS-kod till textilexport.",
  }));

  // 4) Direct / unknown
  bucket.push(makeLead({
    scenario: "4. Direct / okänd",
    day: 22, hour: 16, minute: 3,
    kanal: "Direkt", kampanj: "", sokord: "",
    tjanst: "Rådgivning", landning: "/",
    first_landing_page: "/",
    form_page: "/kontakt",
    source_url: "https://easytull.se/kontakt",
    traffic_source: "(direct)", traffic_medium: "(none)",
    kontakt: "Telefon",
    status: "Pågående", ordervarde: 0,
    anteckning: "Skrev in domänen direkt. Kommer från artikel i Dagens Industri (kunden berättade muntligt).",
  }));

  // 5) Referral
  bucket.push(makeLead({
    scenario: "5. Referral från Tullverket",
    day: 11, hour: 10, minute: 30,
    kanal: "Direkt", kalla: "Tullverket",
    tjanst: "Ursprungsintyg", landning: "/tjanster/ursprungsintyg",
    first_landing_page: "/tjanster/ursprungsintyg",
    form_page: "/tjanster/ursprungsintyg",
    source_url: "https://easytull.se/tjanster/ursprungsintyg",
    original_referrer: "https://www.tullverket.se/foretagsservice/",
    traffic_source: "tullverket.se", traffic_medium: "referral",
    status: "Konverterad", ordervarde: 3400,
    anteckning: "Tullverkets företagsservice länkar till oss för EUR.1.",
  }));

  // 6) Only Source URL available — no referrer, no UTM
  bucket.push(makeLead({
    scenario: "6. Endast Source URL",
    day: 19, hour: 13, minute: 50,
    kanal: "Direkt", tjanst: "Transit", landning: "/tjanster/transit",
    source_url: "https://easytull.se/tjanster/transit",
    form_page: "/tjanster/transit",
    // ingen first_landing_page, ingen referrer
    status: "Informationsförfrågan",
    anteckning: "Kom in på undersida men vi vet inte hur de hittade oss.",
  }));

  // 7) Attribution helt saknad
  bucket.push(makeLead({
    scenario: "7. Helt saknad attribution",
    day: 24, hour: 9, minute: 11,
    kanal: "Direkt", tjanst: "Import",
    // Allt null/tomt
    landning: null,
    status: "Ej svarat",
    anteckning: "E-post utan trackingdata — kanske från äldre cachade länkar.",
  }));

  // 8) Manuellt skapat telefonlead
  bucket.push(makeLead({
    scenario: "8. Manuellt telefonlead",
    day: 21, hour: 8, minute: 45,
    kanal: "Direkt", kalla: "Manuell",
    tjanst: "Rådgivning", landning: null,
    kontakt: "Telefon",
    // Manuell override
    attribution_override: { kalla: "Manuell", confidence: "Okänd" },
    status: "Konverterad", ordervarde: 8900,
    anteckning: "Ringde direkt till Anders mobil. Manuellt registrerad efteråt.",
  }));
})();

// =====================================================================
//  Period model — resolves a period spec into a concrete range, label
//  and previous-period comparison range.
// =====================================================================

// Treat "today" as a deterministic date for the demo (matches _TODAY_FOR_PERIODS).
// Bumped to 14 maj 2026.
const TODAY = new Date(2026, 4, 14);

function isoDate(d){
  const y = d.getFullYear();
  const m = String(d.getMonth()+1).padStart(2,"0");
  const dd = String(d.getDate()).padStart(2,"0");
  return `${y}-${m}-${dd}`;
}
function addDays(d, n){ const x = new Date(d); x.setDate(x.getDate()+n); return x; }
function startOfMonth(d){ return new Date(d.getFullYear(), d.getMonth(), 1); }
function endOfMonth(d){ return new Date(d.getFullYear(), d.getMonth()+1, 0); }
function monthLabel(d){ return `${MONTHS_SV[d.getMonth()]} ${d.getFullYear()}`; }
function shortDate(d){ return `${String(d.getDate()).padStart(2,"0")} ${MONTHS_SV[d.getMonth()].slice(0,3).toLowerCase()}`; }
function diffDays(a, b){ return Math.round((b - a) / 86400000) + 1; }

const PERIOD_PRESETS = [
  { kind:"this-month",   label:"Denna månad" },
  { kind:"prev-month",   label:"Föregående månad" },
  { kind:"last-7",       label:"Senaste 7 dagar" },
  { kind:"last-30",      label:"Senaste 30 dagar" },
  { kind:"last-90",      label:"Senaste 90 dagar" },
];

// Spec → resolved period
function resolvePeriod(spec){
  let from, to, label, shortLabel, isCalendarMonth = false, monthKey = null;
  const today = TODAY;

  if(spec.kind === "month"){
    const [y,m] = spec.value.split("-").map(Number);
    const d = new Date(y, m, 1);
    from = startOfMonth(d);
    to   = endOfMonth(d);
    label = monthLabel(d);
    shortLabel = label;
    isCalendarMonth = true;
    monthKey = `${y}-${m}`;
  }
  else if(spec.kind === "this-month"){
    from = startOfMonth(today);
    to   = today;
    isCalendarMonth = (today.getDate() === endOfMonth(today).getDate());
    label = `${monthLabel(today)} (hittills)`;
    shortLabel = "Denna månad";
    monthKey = `${today.getFullYear()}-${today.getMonth()}`;
  }
  else if(spec.kind === "prev-month"){
    const d = new Date(today.getFullYear(), today.getMonth()-1, 1);
    from = startOfMonth(d);
    to   = endOfMonth(d);
    isCalendarMonth = true;
    label = monthLabel(d);
    shortLabel = "Föregående månad";
    monthKey = `${d.getFullYear()}-${d.getMonth()}`;
  }
  else if(spec.kind === "last-7" || spec.kind === "last-30" || spec.kind === "last-90"){
    const n = spec.kind === "last-7" ? 7 : spec.kind === "last-30" ? 30 : 90;
    to = today;
    from = addDays(today, -(n-1));
    label = `Senaste ${n} dagar (${shortDate(from)}–${shortDate(to)})`;
    shortLabel = `Senaste ${n} dagar`;
  }
  else if(spec.kind === "custom"){
    from = new Date(spec.from + "T00:00:00");
    to   = new Date(spec.to + "T00:00:00");
    if(to < from){ const t = from; from = to; to = t; }
    label = `${shortDate(from)} – ${shortDate(to)} ${from.getFullYear()}${to.getFullYear() !== from.getFullYear() ? "/"+to.getFullYear() : ""}`;
    shortLabel = "Anpassat";
    if(from.getDate() === 1 && to.getTime() === endOfMonth(to).getTime() && from.getMonth() === to.getMonth() && from.getFullYear() === to.getFullYear()){
      isCalendarMonth = true;
      monthKey = `${from.getFullYear()}-${from.getMonth()}`;
      label = monthLabel(from);
    }
  } else {
    // fallback
    from = startOfMonth(today);
    to = endOfMonth(today);
    isCalendarMonth = true;
    label = monthLabel(today);
    shortLabel = label;
    monthKey = `${today.getFullYear()}-${today.getMonth()}`;
  }

  const days = diffDays(from, to);
  // Months touched
  const monthKeys = [];
  let cursor = startOfMonth(from);
  while(cursor <= to){
    monthKeys.push(`${cursor.getFullYear()}-${cursor.getMonth()}`);
    cursor = new Date(cursor.getFullYear(), cursor.getMonth()+1, 1);
  }

  // Previous comparable period
  let pFrom, pTo, pLabel;
  if(isCalendarMonth){
    const d = new Date(from.getFullYear(), from.getMonth()-1, 1);
    pFrom = startOfMonth(d);
    pTo   = endOfMonth(d);
    pLabel = monthLabel(d);
  } else {
    pTo   = addDays(from, -1);
    pFrom = addDays(pTo, -(days-1));
    pLabel = `${shortDate(pFrom)}–${shortDate(pTo)}`;
  }

  return {
    spec,
    from, to,
    fromIso: isoDate(from),
    toIso:   isoDate(to),
    days,
    label, shortLabel,
    isCalendarMonth,
    monthKey,
    monthKeys,
    prev: {
      from: pFrom, to: pTo,
      fromIso: isoDate(pFrom), toIso: isoDate(pTo),
      label: pLabel,
      days: diffDays(pFrom, pTo),
    },
  };
}

// Months available in the data, newest first — for the month list in the picker.
function listAvailableMonths(){
  return PERIODS.slice().reverse().map(p => ({
    monthKey: `${p.year}-${p.month}`,
    specValue: `${p.year}-${p.month}`,
    label: p.label,
  }));
}

// Iterate all lead months in DB and filter by [fromIso, toIso]
function getLeadsInRange(market, fromIso, toIso){
  const out = [];
  Object.keys(LEADS_DB).forEach(key => {
    const bucket = LEADS_DB[key];
    if(market === "BOTH"){
      bucket.SE.forEach(l => { if(l.datum >= fromIso && l.datum <= toIso) out.push(l); });
      bucket.NO.forEach(l => { if(l.datum >= fromIso && l.datum <= toIso) out.push(l); });
    } else {
      bucket[market].forEach(l => { if(l.datum >= fromIso && l.datum <= toIso) out.push(l); });
    }
  });
  return out;
}

// Budget for a resolved period. Pro-rates fixed retainers and Google Ads
// across months by overlap days. Setup costs included only if dated inside
// the period. Returns per-market or combined (BOTH) structure.
function getBudgetForResolvedPeriod(market, resolved){
  function calcForMarket(mkt){
    let googleAds = 0, seo = 0, adsManagement = 0, ovrigt = 0;
    let monthlyBudget = 0;
    let isPartial = false;
    const setupItems = [];

    resolved.monthKeys.forEach(mk => {
      const b = BUDGET_DB[mk]?.[mkt];
      if(!b) return;
      // Overlap days in this month
      const [y,m] = mk.split("-").map(Number);
      const mFrom = new Date(y, m, 1);
      const mTo   = endOfMonth(mFrom);
      const oFrom = resolved.from > mFrom ? resolved.from : mFrom;
      const oTo   = resolved.to   < mTo   ? resolved.to   : mTo;
      const overlapDays = diffDays(oFrom, oTo);
      const monthDays = diffDays(mFrom, mTo);
      const frac = overlapDays / monthDays;
      if(frac < 1) isPartial = true;

      googleAds     += b.googleAds * frac;
      seo           += b.seo * frac;
      adsManagement += (b.adsManagement || 0) * frac;
      ovrigt        += b.ovrigt * frac;
      monthlyBudget += b.googleAds + b.seo + (b.adsManagement || 0) + b.ovrigt;

      (b.setup || []).forEach(s => {
        if(s.datum >= resolved.fromIso && s.datum <= resolved.toIso){
          setupItems.push({ ...s, market: mkt });
        }
      });
    });

    googleAds = Math.round(googleAds);
    seo = Math.round(seo);
    adsManagement = Math.round(adsManagement);
    ovrigt = Math.round(ovrigt);
    monthlyBudget = Math.round(monthlyBudget);
    const variable = googleAds + ovrigt;
    const fixed    = seo + adsManagement;
    const ongoing  = variable + fixed;
    const setup    = setupItems.reduce((a,s)=>a+s.kostnad, 0);

    return {
      market: mkt,
      googleAds, seo, adsManagement, ovrigt,
      variable, fixed, ongoing, setup, setupItems,
      total: ongoing + setup,
      monthlyBudget,
      isPartial,
    };
  }

  if(market === "BOTH"){
    const se = calcForMarket("SE");
    const no = calcForMarket("NO");
    return {
      SE: se, NO: no,
      combined: {
        googleAds: se.googleAds + no.googleAds,
        seo: se.seo + no.seo,
        adsManagement: se.adsManagement + no.adsManagement,
        ovrigt: se.ovrigt + no.ovrigt,
        variable: se.variable + no.variable,
        fixed: se.fixed + no.fixed,
        ongoing: se.ongoing + no.ongoing,
        setup: se.setup + no.setup,
        setupItems: [...se.setupItems, ...no.setupItems],
        total: se.total + no.total,
        monthlyBudget: se.monthlyBudget + no.monthlyBudget,
        isPartial: se.isPartial || no.isPartial,
      }
    };
  }
  return calcForMarket(market);
}

// Get budget for prev range (used in delta calculations)
function getBudgetForResolved(market, resolved){
  if(market === "BOTH") return getBudgetForResolvedPeriod(market, resolved).combined;
  return getBudgetForResolvedPeriod(market, resolved);
}

const ANVANDARE = [
  { namn: "Easytull", roll: "Admin", rolltyp: "Admin", email: "easytull@gmail.com", sistInne: "Idag", aktiv: true },
];

function userFromEmail(email, roleType="Redaktör"){
  const clean = String(email || "").trim().toLowerCase();
  const name = clean.split("@")[0] || "Användare";
  return {
    namn: name.charAt(0).toUpperCase() + name.slice(1),
    roll: roleType,
    rolltyp: roleType,
    email: clean,
    sistInne: "Inbjuden",
    aktiv: true,
  };
}

// =====================================================================
//  Privat ekonomi — kostnader + fakturor (intern överblick, ej bokföring)
// =====================================================================
const EKONOMI_KATEGORIER = ["Google Ads","SEO","Ads management","Hosting","Programvara","Telefon","Byrå","Domäner","Övrigt"];
const KOSTNADSTYPER       = ["Återkommande","Engångskostnad","Anpassad period","Månadsbudget"];
const FAKTURA_STATUSAR    = ["Utkast","Skickad","Betald","Förfallen","Delbetald"];
const FAKTURA_TJANSTER    = ["Import","Export","Transit","HS-kod","Ursprungsintyg","Rådgivning","Annat"];
const BETALSATT           = ["Bank","Stripe","Kort","Swish","Vipps","Annat"];

// Generera mock-kostnader per månad i PERIODS
const KOSTNADER = [];
let _kid = 1;
function pushKostnad(o){ KOSTNADER.push({ id: `k-${_kid++}`, ...o }); }

PERIODS.forEach((p, idx) => {
  const ym = `${p.year}-${String(p.month+1).padStart(2,"0")}`;
  // Återkommande månadskostnader — fakturerade kring den 1:a
  pushKostnad({ datum:`${ym}-02`, marknad:"EasyTull SE", kategori:"Google Ads",      leverantor:"Google Ireland Ltd", beskrivning:"Google Ads · konto SE", belopp: Math.round(38000*(1+idx*0.04)), valuta:"SEK", moms:25, kostnadstyp:"Återkommande", periodFran:`${ym}-01`, periodTill:`${ym}-${daysInMonth(p.year,p.month)}`, betald:true,  betaldatum:`${ym}-12`, kommentar:"" });
  pushKostnad({ datum:`${ym}-02`, marknad:"EasyToll NO", kategori:"Google Ads",      leverantor:"Google Ireland Ltd", beskrivning:"Google Ads · konto NO", belopp: Math.round(24000*(1+idx*0.04)), valuta:"NOK", moms:25, kostnadstyp:"Återkommande", periodFran:`${ym}-01`, periodTill:`${ym}-${daysInMonth(p.year,p.month)}`, betald:true,  betaldatum:`${ym}-12`, kommentar:"" });
  pushKostnad({ datum:`${ym}-05`, marknad:"EasyTull SE", kategori:"SEO",             leverantor:"Pineberry AB",       beskrivning:"SEO-retainer · SE",  belopp: 15000, valuta:"SEK", moms:25, kostnadstyp:"Återkommande", periodFran:`${ym}-01`, periodTill:`${ym}-${daysInMonth(p.year,p.month)}`, betald: idx < 2, betaldatum: idx < 2 ? `${ym}-15` : null, kommentar:"" });
  pushKostnad({ datum:`${ym}-05`, marknad:"EasyToll NO", kategori:"SEO",             leverantor:"Pineberry AB",       beskrivning:"SEO-retainer · NO",  belopp: 12000, valuta:"NOK", moms:25, kostnadstyp:"Återkommande", periodFran:`${ym}-01`, periodTill:`${ym}-${daysInMonth(p.year,p.month)}`, betald: idx < 2, betaldatum: idx < 2 ? `${ym}-15` : null, kommentar:"" });
  pushKostnad({ datum:`${ym}-05`, marknad:"EasyTull SE", kategori:"Ads management",  leverantor:"Pineberry AB",       beskrivning:"Ads mgmt · SE",      belopp: 6500,  valuta:"SEK", moms:25, kostnadstyp:"Återkommande", periodFran:`${ym}-01`, periodTill:`${ym}-${daysInMonth(p.year,p.month)}`, betald: idx < 2, betaldatum: idx < 2 ? `${ym}-15` : null, kommentar:"" });
  pushKostnad({ datum:`${ym}-05`, marknad:"EasyToll NO", kategori:"Ads management",  leverantor:"Pineberry AB",       beskrivning:"Ads mgmt · NO",      belopp: 5000,  valuta:"NOK", moms:25, kostnadstyp:"Återkommande", periodFran:`${ym}-01`, periodTill:`${ym}-${daysInMonth(p.year,p.month)}`, betald: idx < 2, betaldatum: idx < 2 ? `${ym}-15` : null, kommentar:"" });
  pushKostnad({ datum:`${ym}-01`, marknad:"Gemensam",    kategori:"Hosting",         leverantor:"Cloudflare Pages",   beskrivning:"Hosting + CDN",      belopp: 290,   valuta:"SEK", moms:25, kostnadstyp:"Återkommande", periodFran:`${ym}-01`, periodTill:`${ym}-${daysInMonth(p.year,p.month)}`, betald:true,  betaldatum:`${ym}-01`, kommentar:"" });
  pushKostnad({ datum:`${ym}-03`, marknad:"Gemensam",    kategori:"Programvara",     leverantor:"Fortnox AB",         beskrivning:"Bokföring + faktura",belopp: 549,   valuta:"SEK", moms:25, kostnadstyp:"Återkommande", periodFran:`${ym}-01`, periodTill:`${ym}-${daysInMonth(p.year,p.month)}`, betald:true,  betaldatum:`${ym}-03`, kommentar:"" });
  pushKostnad({ datum:`${ym}-04`, marknad:"Gemensam",    kategori:"Programvara",     leverantor:"Fluent Forms Pro",   beskrivning:"Lifetime add-ons (årlig)", belopp: 89, valuta:"EUR", moms:25, kostnadstyp:"Återkommande", periodFran:`${ym}-01`, periodTill:`${ym}-${daysInMonth(p.year,p.month)}`, betald:true,  betaldatum:`${ym}-04`, kommentar:"" });
  pushKostnad({ datum:`${ym}-07`, marknad:"Gemensam",    kategori:"Telefon",         leverantor:"Tre Sverige",        beskrivning:"Mobilabonnemang",    belopp: 449,   valuta:"SEK", moms:25, kostnadstyp:"Återkommande", periodFran:`${ym}-01`, periodTill:`${ym}-${daysInMonth(p.year,p.month)}`, betald:true,  betaldatum:`${ym}-07`, kommentar:"" });
});
// Engångskostnader per startmånad / mid-period
pushKostnad({ datum:"2026-03-08", marknad:"EasyTull SE", kategori:"Byrå",      leverantor:"Pineberry AB",       beskrivning:"Onboarding & setup · SE",     belopp:18000, valuta:"SEK", moms:25, kostnadstyp:"Engångskostnad",  periodFran:"2026-03-08", periodTill:"2026-03-08", betald:true,  betaldatum:"2026-03-15", kommentar:"Uppstart" });
pushKostnad({ datum:"2026-03-15", marknad:"EasyToll NO", kategori:"Byrå",      leverantor:"Pineberry AB",       beskrivning:"Onboarding NO",               belopp:12000, valuta:"NOK", moms:25, kostnadstyp:"Engångskostnad",  periodFran:"2026-03-15", periodTill:"2026-03-15", betald:true,  betaldatum:"2026-03-22", kommentar:"Uppstart" });
pushKostnad({ datum:"2026-03-02", marknad:"Gemensam",    kategori:"Domäner",   leverantor:"Loopia AB",          beskrivning:"easytull.se + easytoll.no (årlig)", belopp:2400, valuta:"SEK", moms:25, kostnadstyp:"Engångskostnad", periodFran:"2026-03-02", periodTill:"2027-03-02", betald:true,  betaldatum:"2026-03-02", kommentar:"Förnyelse" });
pushKostnad({ datum:"2026-04-12", marknad:"EasyTull SE", kategori:"Byrå",      leverantor:"Pineberry AB",       beskrivning:"Ny landningssida · HS-kod",   belopp:8500,  valuta:"SEK", moms:25, kostnadstyp:"Engångskostnad",  periodFran:"2026-04-12", periodTill:"2026-04-12", betald:true,  betaldatum:"2026-04-20", kommentar:"" });
pushKostnad({ datum:"2026-05-04", marknad:"EasyToll NO", kategori:"Byrå",      leverantor:"Pineberry AB",       beskrivning:"GA4 + GTM omkonfigurering · NO", belopp:6000, valuta:"NOK", moms:25, kostnadstyp:"Engångskostnad",  periodFran:"2026-05-04", periodTill:"2026-05-04", betald:false, betaldatum:null,        kommentar:"Faktura ej mottagen ännu" });

// Mock-fakturor / intäkter — kopplade till konverterade leads där det är möjligt
const INTAKTER = [];
let _iid = 1;
function pushIntakt(o){ INTAKTER.push({ id: `i-${_iid++}`, ...o }); }
const KUNDER_SE = ["Bornholm Logistics AB","Nordic Textiles AB","Falck Foods AB","Götaverken Marine","Volvo Stockholm AB","Hedlunds Maskin AB","Stenströms Skjorter","Skanska Stål AB"];
const KUNDER_NO = ["Bergen Frakt AS","Oslo Trading AS","Stavanger Marine AS","Nordlys Tekstil AS","Lofoten Fisk AS"];

// Skapa intäkter baserat på konverterade leads
function buildIntakterFromLeads(){
  Object.values(LEADS_DB).forEach(bucket => {
    ["SE","NO"].forEach(mkt => {
      bucket[mkt].forEach(l => {
        if(l.status !== "Konverterad" || !l.ordervarde) return;
        // Random status distribution
        const r = mulberry32(Number(l.id.replace(/\D/g, "").slice(-6) || 1))();
        let status, betaldatum=null, betaltBelopp=0, betalsatt=null;
        if(r < 0.55){ status="Betald"; const d = new Date(l.datum); d.setDate(d.getDate()+14+Math.floor(r*10)); betaldatum=isoDate(d); betaltBelopp=l.ordervarde; betalsatt = r<0.3?"Bank":r<0.5?"Stripe":"Bank"; }
        else if(r < 0.78){ status="Skickad"; }
        else if(r < 0.85){ status="Förfallen"; }
        else if(r < 0.92){ status="Delbetald"; betaltBelopp = Math.round(l.ordervarde*0.5); const d = new Date(l.datum); d.setDate(d.getDate()+10); betaldatum=isoDate(d); betalsatt="Bank"; }
        else { status="Utkast"; }
        const kund = mkt === "SE" ? KUNDER_SE[Math.floor(r*KUNDER_SE.length)] : KUNDER_NO[Math.floor(r*KUNDER_NO.length)];
        const tjanstMap = { "HS-kod":"HS-kod","Ursprungsintyg":"Ursprungsintyg","Import":"Import","Export":"Export","Transit":"Transit","Rådgivning":"Rådgivning" };
        pushIntakt({
          datum: l.datum,
          marknad: mkt === "SE" ? "EasyTull SE" : "EasyToll NO",
          kund,
          tjanst: tjanstMap[l.tjanst] || "Annat",
          fakturanr: `INV-${l.datum.slice(0,4)}${l.datum.slice(5,7)}-${String(_iid).padStart(3,"0")}`,
          belopp: l.ordervarde,
          valuta: mkt === "SE" ? "SEK" : "NOK",
          moms: 25,
          status, betaldatum, betaltBelopp, betalsatt,
          kopplatLead: l.id,
          kommentar: ""
        });
      });
    });
  });
}
buildIntakterFromLeads();

Object.keys(LEADS_DB).forEach(key => {
  LEADS_DB[key].SE = [];
  LEADS_DB[key].NO = [];
});
KOSTNADER.length = 0;
INTAKTER.length = 0;

const MAL = {
  SE: { budget: 60000, leads: 50, konvertering: 28 },
  NO: { budget: 40000, leads: 35, konvertering: 25 },
};

function upsertLeadIntoDb(lead){
  if(!lead || !lead.id || !lead.datum || !lead.land) return;
  const [y,m] = lead.datum.split("-").map(Number);
  const key = `${y}-${m-1}`;
  if(!LEADS_DB[key]) LEADS_DB[key] = { SE: [], NO: [] };
  if(!LEADS_DB[key][lead.land]) LEADS_DB[key][lead.land] = [];
  const bucket = LEADS_DB[key][lead.land];
  const existing = bucket.find(l => l.id === lead.id);
  if(existing) Object.assign(existing, lead);
  else bucket.push(lead);
}

function upsertById(list, item){
  if(!item || !item.id) return;
  const existing = list.find(x => x.id === item.id);
  if(existing) Object.assign(existing, item);
  else list.push(item);
}

function hydrateFromSupabase(payload){
  const state = { leadsState:{}, budgetState:{}, kostnaderState:{}, intakterState:{}, mal:null, valuta:null, users:null };
  if(!payload || !payload.configured) return state;

  (payload.leads || []).forEach(lead => {
    upsertLeadIntoDb(lead);
    state.leadsState[lead.id] = lead;
  });

  (payload.budgets || []).forEach(budget => {
    const [monthKey, market] = String(budget.id || "").split(":");
    if(monthKey && market && BUDGET_DB[monthKey]?.[market]){
      Object.assign(BUDGET_DB[monthKey][market], budget);
      state.budgetState[`${monthKey}-${market}`] = budget;
    }
  });

  (payload.costs || []).forEach(cost => {
    upsertById(KOSTNADER, cost);
    state.kostnaderState[cost.id] = cost;
  });

  (payload.revenues || []).forEach(revenue => {
    upsertById(INTAKTER, revenue);
    state.intakterState[revenue.id] = revenue;
  });

  if(payload.settings){
    if(payload.settings.mal) state.mal = payload.settings.mal;
    if(payload.settings.valuta) state.valuta = payload.settings.valuta;
    if(payload.settings.users) state.users = payload.settings.users;
  }

  return state;
}

// Expose globally for cross-file scripts
Object.assign(window, {
  MONTHS_SV, VECKODAGAR, VECKODAGAR_FULL,
  TJANSTER, STATUSAR, KONTAKTSATT, KANALER_SE, KANALER_NO,
  PERIODS, LEADS_DB, BUDGET_DB, ANVANDARE, MAL,
  daysInMonth,
  KALLA_KATEGORIER, CONFIDENCE_NIVAER,
  classifyAttribution, buildTracking,
  TODAY, PERIOD_PRESETS, resolvePeriod, listAvailableMonths,
  getLeadsInRange, getBudgetForResolvedPeriod, getBudgetForResolved,
  isoDate, addDays, startOfMonth, endOfMonth, monthLabel, shortDate, diffDays,
  KOSTNADER, INTAKTER, EKONOMI_KATEGORIER, KOSTNADSTYPER, FAKTURA_STATUSAR, FAKTURA_TJANSTER, BETALSATT,
  upsertLeadIntoDb, upsertById, hydrateFromSupabase, userFromEmail,
});
