const { useState: useStateA } = React;

/* Searchable category dropdown (combobox). Filters by the label in the ACTIVE language,
   grouped by sector, keyboard-navigable. Stores the category id via onChange — same as the
   old <select> (wizard logic unchanged). If the stored id no longer exists, it just shows
   the placeholder (no crash). Re-renders on language toggle because App re-renders. */
function CategoryCombobox({ value, onChange }){
  const T = window.T;
  const groups = window.getCategoryGroups();
  const norm = (s)=> (s||'').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'');
  const [open, setOpen] = useStateA(false);
  const [query, setQuery] = useStateA('');
  const [active, setActive] = useStateA(0);

  let selectedLabel = '';
  groups.forEach(g=> g.items.forEach(it=>{ if(it.id===value) selectedLabel = it.label; }));

  const q = norm(query);
  const aliasMap = window.CATEGORY_ALIASES || {};
  const matches = (it)=>{
    if(!q) return true;
    if(norm(it.label).indexOf(q) >= 0) return true;            // visible label
    const al = aliasMap[it.id];                                 // hidden search aliases (if any)
    return !!al && al.some(a=> norm(a).indexOf(q) >= 0);
  };
  const fGroups = groups
    .map(g=>({ ...g, items: g.items.filter(matches) }))
    .filter(g=> g.items.length);
  const flat = [];
  fGroups.forEach(g=> g.items.forEach(it=> flat.push(it)));
  const act = flat.length ? Math.max(0, Math.min(active, flat.length-1)) : -1;

  const choose = (it)=>{ if(!it) return; onChange(it.id); setQuery(''); setOpen(false); };
  const onKey = (e)=>{
    if(e.key==='ArrowDown'){ e.preventDefault(); setOpen(true); setActive(a=>Math.min(a+1, flat.length-1)); }
    else if(e.key==='ArrowUp'){ e.preventDefault(); setActive(a=>Math.max(a-1, 0)); }
    else if(e.key==='Enter'){ if(open){ e.preventDefault(); choose(flat[act]); } }
    else if(e.key==='Escape'){ setOpen(false); }
  };

  return (
    <div className="combo">
      <input className="input combo-input" type="text" spellCheck={false}
        role="combobox" aria-expanded={open} aria-autocomplete="list" aria-controls="cat-pop"
        placeholder={T('camp.basics.catPlaceholder')}
        value={open ? query : selectedLabel}
        onFocus={()=>{ setOpen(true); setQuery(''); setActive(0); }}
        onChange={e=>{ setQuery(e.target.value); setOpen(true); setActive(0); }}
        onKeyDown={onKey}
        onBlur={()=> setTimeout(()=> setOpen(false), 120)}/>
      <span className="combo-caret" aria-hidden="true">▾</span>
      {open && (
        <div className="combo-pop" id="cat-pop" role="listbox">
          {flat.length===0 && <div className="combo-empty">{T('camp.catNoResults')}</div>}
          {fGroups.map(g=>(
            <div className="combo-group" key={g.group||'_other'}>
              {g.label && <div className="combo-gh">{g.label}</div>}
              {g.items.map(it=>{
                const idx = flat.indexOf(it);
                return (
                  <div key={it.id} role="option" aria-selected={value===it.id}
                    className={"combo-opt"+(idx===act?' active':'')+(value===it.id?' sel':'')}
                    onMouseDown={(e)=>{ e.preventDefault(); choose(it); }}
                    onMouseEnter={()=> setActive(idx)}>
                    {it.label}
                  </div>
                );
              })}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

/* ---------- Step 1: Basics ---------- */
function StepBasics({ data, set }){
  const I = window.Icons;
  const T = window.T;
  return (
    <div className="card step-fade">
      <div className="step-head">
        <div className="eyebrow">{T('camp.eyebrow.s1')}</div>
        <h2>{T('camp.basics.title')}</h2>
        <p>{T('camp.basics.sub')}</p>
      </div>

      <div className="field">
        <label>{T('camp.basics.nameLabel')}</label>
        <input className="input" placeholder={T('camp.basics.namePh')}
          value={data.name} onChange={e=>set({name:e.target.value})}/>
      </div>

      <div className="field">
        <label>{T('camp.basics.catLabel')}</label>
        <CategoryCombobox value={data.category} onChange={id=>set({category:id})}/>
      </div>

      <div className="field">
        <label>{T('camp.basics.goalLabel')} <span className="hint">{T('camp.basics.goalHint')}</span></label>
        <div className="opt-grid cols2">
          {window.getGoals().map(g=>{
            const C = I[g.icon];
            return (
              <button key={g.id} className={"opt"+(data.goal===g.id?" sel":"")} onClick={()=>set({goal:g.id})}>
                <span className="check"><I.checkSm/></span>
                <div className="opt-ic"><C w={20}/></div>
                <h4>{g.name}</h4>
                <p>{g.desc}</p>
              </button>
            );
          })}
        </div>
      </div>

      {/* Ad language = the language of the radio SPOT (campaign data), NOT the UI language. Selector logic/options untouched. */}
      <div className="field" style={{marginBottom:0}}>
        <label>{T('camp.basics.adLangLabel')} <span className="hint">{T('camp.basics.adLangHint')}</span></label>
        <div className="chips">
          {window.LANGS.map(l=>{
            const on = data.langs.includes(l.id);
            return (
              <button key={l.id} className={"chip"+(on?" sel":"")}
                onClick={()=>set({langs: on ? data.langs.filter(x=>x!==l.id) : [...data.langs,l.id]})}>
                {on && <I.checkSm/>}{l.name}
              </button>
            );
          })}
        </div>
      </div>
    </div>
  );
}

/* ---------- Step 2: Package ---------- */
function StepPackage({ data, set }){
  const I = window.Icons;
  const T = window.T;
  return (
    <div className="card step-fade">
      <div className="step-head">
        <div className="eyebrow">{T('camp.eyebrow.s2')}</div>
        <h2>{T('camp.pkg.title')}</h2>
        <p>{T('camp.pkg.sub')}</p>
      </div>
      <div className="opt-grid cols4">
        {window.getPackages().map(p=>(
          <div key={p.id} className={"pkg"+(p.popular?" popular":"")+(data.pkg===p.id?" sel":"")}
            onClick={()=>set({pkg:p.id, stations:{}})}>
            {p.popular && <div className="pkg-tag">{T('pkg.popular')}</div>}
            <span className="check"><I.checkSm/></span>
            <h3>{p.name}</h3>
            <div className="pkg-sub">{p.sub}</div>
            <div className="price">{p.price!=null?('$'+p.price):T('pkg.price.custom')}{p.price!=null && <small>{T('pkg.month')}</small>}</div>
            <ul>
              {p.feats.map(f=><li key={f}><I.check w={15}/>{f}</li>)}
            </ul>
          </div>
        ))}
      </div>
      <div className="tiny" style={{marginTop:16,display:'flex',gap:8,alignItems:'center'}}>
        <I.lock w={14}/> {T('camp.pkg.terms')}
      </div>
    </div>
  );
}

/* ---------- Step 3: Stations ---------- */
function StepStations({ data, set }){
  const I = window.Icons;
  const T = window.T;
  const norm = (s)=> (s||'').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'');
  const pkg = window.getPackages().find(p=>p.id===data.pkg);
  const maxMarkets = pkg ? pkg.markets : 1;

  const [idx, setIdx] = useStateA(window.MARKETS_INDEX);
  const [, setTick] = useStateA(0);              // bump to re-render when stations finish loading
  const [openState, setOpenState] = useStateA(null);
  const [openMarket, setOpenMarket] = useStateA(null);
  const [query, setQuery] = useStateA('');
  const bump = ()=> setTick(t=>t+1);

  // index up front (lightweight list); station details on demand + cached
  React.useEffect(()=>{ window.loadMarketsIndex().then(j=>setIdx(j)); }, []);
  React.useEffect(()=>{ if(Object.keys(data.stations||{}).some(k=>data.stations[k])) window.loadStations().then(bump); }, []);
  React.useEffect(()=>{ if(query) window.loadStations().then(bump); }, [query]);

  const byId = window.STATIONS_BY_ID || {};
  const STA = window.STATIONS || {};
  const selIds = Object.keys(data.stations||{}).filter(id=>data.stations[id]);
  const selMarkets = new Set();
  // a selected station counts toward its LICENSE market (byId.marketId) — consistent with the frozen
  // "one station belongs to one market = its city of license" rule, and inherently double-count-proof.
  selIds.forEach(id=>{ const s = byId[id]; if(s) selMarkets.add(s.marketId); });
  const totalReach = selIds.reduce((a,id)=> a + ((byId[id]&&byId[id].reach)||0), 0);

  // --- Extended coverage (alsoServes): a station lives ONCE in its license market, but its high-power
  // signal can also serve neighbor markets. It surfaces in those neighbor markets, flagged, WITHOUT a
  // second record. Same id = same toggle -> can never be double-counted or double-selected.
  const cityOf = (id)=>{ let c=id; (idx?idx.states:[]).forEach(st=>st.markets.forEach(m=>{ if(m.id===id) c=m.city; })); return c; };
  const extendedFor = (mId)=>{
    const out=[];
    Object.keys(STA).forEach(src=>{ if(src===mId) return; (STA[src]||[]).forEach(s=>{
      if(Array.isArray(s.alsoServes) && s.alsoServes.indexOf(mId)>=0) out.push(Object.assign({}, s, { _extFrom: src }));
    }); });
    return out;
  };
  const marketStations = (mId)=> (STA[mId]||[]).concat(extendedFor(mId));   // own + extended-from-neighbors

  const toggle = (mId, sId)=>{   // mId = the station's LICENSE market (home), passed by StationRow
    const cur = !!data.stations[sId];
    if(!cur){ const wouldAdd = !selMarkets.has(mId); if(wouldAdd && selMarkets.size >= maxMarkets) return; }
    set({ stations: { ...data.stations, [sId]: !cur } });
  };

  const langLabel = (l)=> l ? T('camp.lang.'+l) : '';
  const StationRow = (m, s)=>{
    const on = !!data.stations[s.id];
    const band = s.band || 'FM';
    const home = s._extFrom || m.id;   // toggle/cap always use the LICENSE market, not the viewed one
    // band + dial where heard (for AM->FM translators the FM dial, e.g. "FM 94.9 / 93.7"); language label when known
    const bandDial = s.dial ? band+' '+s.dial : band;
    const meta = [m.city, bandDial, s.lang ? langLabel(s.lang) : null].filter(Boolean).join(' · ');
    return (
      <div key={s.id} className={"station"+(on?" sel":"")+(s._extFrom?" station-ext":"")} onClick={()=>toggle(home, s.id)}>
        <div className="cbx">{on && <I.checkSm/>}</div>
        <div style={{flex:1,minWidth:0}}>
          <div className="st-name">{s.name}{s.call && s.call!==s.name ? <span className="st-call"> · {s.call}</span> : null}</div>
          <div className="st-meta">{meta}</div>
          {s._extFrom && <div className="ext-badge">↗ {T('camp.extCoverage')} · {cityOf(s._extFrom)}</div>}
        </div>
        {s.fmt && <span className="fmt-tag">{s.fmt}</span>}
        <div className="st-reach">
          {s.reach!=null
            ? <><b>{window.fmtNum(s.reach)}</b><span>{T('demo.weeklyReach')}</span></>
            : <><b>#{s.rank||'—'}</b><span>{T('camp.coverage')}</span></>}
        </div>
        {/* "Ficha FCC" button removed from UI (PM: internal technical info). fccLink stays in the data. */}
      </div>
    );
  };

  // one market: header (from index) + its stations (rendered only when open / forced by search)
  const MarketBlock = (st, m, forceOpen, stationsList)=>{
    const isOpen = forceOpen || openMarket===m.id;
    const list = marketStations(m.id);                 // own + extended-coverage stations
    const cnt = list.filter(s=>data.stations[s.id]).length;
    const shownCount = window.STATIONS ? list.length : m.count;   // header reflects extended once loaded
    const locked = !selMarkets.has(m.id) && selMarkets.size >= maxMarkets;
    const rows = isOpen ? (stationsList || list) : null;
    return (
      <div className="market-block" key={m.id} style={locked?{opacity:.55}:null}>
        <div className="market-head" onClick={()=>{ if(openMarket===m.id){ setOpenMarket(null); } else { setOpenMarket(m.id); window.loadStations().then(bump); } }}>
          <div className="mh-l">
            <div className="flag"><I.pin w={17}/></div>
            <div>
              <h4>{m.city}, {st.code}</h4>
              <div className="msub">{shownCount} {T('camp.stations.word')}{m.pop ? ' · '+m.pop+' '+T('camp.listeners') : ''}</div>
            </div>
          </div>
          <div style={{display:'flex',alignItems:'center',gap:12}}>
            {cnt>0 && <span className="mcount">{cnt} {T('camp.selected')}</span>}
            {locked && <span className="tiny">{T('camp.stations.limitReached')}</span>}
            <I.chevD w={18} style={{transform:isOpen?'rotate(180deg)':'none',transition:'.2s',color:'#9298a6'}}/>
          </div>
        </div>
        {isOpen && (rows ? rows.map(s=>StationRow(m, s)) : <div className="tiny" style={{padding:'10px 14px'}}>…</div>)}
      </div>
    );
  };

  const q = norm(query);
  let body;
  if(!idx){
    body = <div className="tiny" style={{padding:'14px',textAlign:'center'}}>…</div>;
  } else if(q){
    const matches = [];
    idx.states.forEach(st=> st.markets.forEach(m=>{
      const all = marketStations(m.id);                // include extended-coverage stations in search
      const marketMatch = norm(m.city).indexOf(q)>=0 || norm(st.name).indexOf(q)>=0 || norm(st.code).indexOf(q)>=0;
      const hit = all.filter(s=> norm(s.name).indexOf(q)>=0 || norm(s.fmt).indexOf(q)>=0 || norm(s.call||'').indexOf(q)>=0 || norm(s.dial||'').indexOf(q)>=0);
      if(marketMatch) matches.push({st,m,stations:all});
      else if(hit.length) matches.push({st,m,stations:hit});
    }));
    body = matches.length
      ? matches.map(x=> MarketBlock(x.st, x.m, true, x.stations))
      : <div className="tiny" style={{padding:'14px',textAlign:'center'}}>{T('camp.catNoResults')}</div>;
  } else {
    body = idx.states.map(st=>{
      const sOpen = openState===st.code;
      return (
        <div className="market-block" key={st.code}>
          <div className="market-head" onClick={()=>setOpenState(sOpen?null:st.code)}>
            <div className="mh-l">
              <div className="flag"><I.pin w={17}/></div>
              <div>
                <h4>{st.name}</h4>
                <div className="msub">{st.markets.length} {st.markets.length>1?T('camp.market.many'):T('camp.market.one')}</div>
              </div>
            </div>
            <I.chevD w={18} style={{transform:sOpen?'rotate(180deg)':'none',transition:'.2s',color:'#9298a6'}}/>
          </div>
          {sOpen && <div className="market-sub">{st.markets.map(m=> MarketBlock(st, m, false, null))}</div>}
        </div>
      );
    });
  }

  return (
    <div className="card step-fade">
      <div className="step-head">
        <div className="eyebrow">{T('camp.eyebrow.s3')}</div>
        <h2>{T('camp.stations.title')}</h2>
        <p>{T('camp.stations.cov1')} <b style={{color:'var(--ink)'}}>{pkg?pkg.name:''}</b> {T('camp.stations.cov2')} <b style={{color:'var(--ink)'}}>{maxMarkets} {maxMarkets>1?T('camp.market.many'):T('camp.market.one')}</b>{T('camp.stations.cov3')}</p>
      </div>

      <input className="input" style={{marginBottom:14}} type="text" spellCheck={false}
        placeholder={T('camp.stations.searchPh')} value={query}
        onChange={e=>setQuery(e.target.value)}/>

      {body}

      <div className="tally">
        <div className="ti"><span>{T('camp.tally.stations')}</span><b>{selIds.length}</b></div>
        <div className="ti"><span>{T('camp.tally.markets')}</span><b>{selMarkets.size} <span style={{fontSize:12,color:'#9aa1ad',fontWeight:600}}>/ {maxMarkets}</span></b></div>
        <div className="ti"><span>{T('camp.tally.reach')}</span><b className="v">{totalReach?window.fmtNum(totalReach):'—'}</b></div>
        <div className="ti" style={{textAlign:'right'}}><span>{T('camp.tally.spots')}</span><b>{pkg?Math.round(pkg.spots/4):0}</b></div>
      </div>
    </div>
  );
}

Object.assign(window, { StepBasics, StepPackage, StepStations });
