// DVC Helper page: 2026 points planner.
// Ported from the standalone dvchelper_mock.html into the React SPA.
// Data + chart math live here; visual styles are scoped under .dvc-helper in index.html.

/* ─────────────────────────────────────────────────────────────
   DATA — 2026 points (Deluxe Studio, per night, [TP1..TP7])
   ───────────────────────────────────────────────────────────── */
const DVC_PARKS = [
  { id:'wdw', name:'Disney World',     sub:'Florida',   rgb:'37,99,235',  pos:'20% 28%' },
  { id:'dlr', name:'Disneyland',       sub:'California', rgb:'219,39,119', pos:'80% 24%' },
  { id:'tds', name:'Tokyo DisneySea',  sub:'Japan',     rgb:'13,148,136', pos:'50% 72%' },
  { id:'dlp', name:'Disneyland Paris', sub:'France',    rgb:'124,58,237', pos:'14% 80%' },
  { id:'shd', name:'Shanghai Disney',  sub:'China',     rgb:'234,88,12',  pos:'86% 78%' },
];
const DVC_PARK = Object.fromEntries(DVC_PARKS.map(p => [p.id, p]));

/* 2026 DVC points chart — lowest-view Deluxe Studio, per night, [TP1..TPn].
   Values transcribed directly from disney_points_charts_2026.pdf.
   `cal` selects the resort's travel-period calendar (length must match arrays). */
const DVC_RESORTS = [
  // ── Walt Disney World (standard 7-period WDW calendar) ──
  { park:'wdw', cal:'wdw', room:'Deluxe Studio · Resort',   name:"Polynesian Villas & Bungalows",   std:[14,17,19,20,22,25,34], pk:[17,20,22,23,25,28,36] },
  { park:'wdw', cal:'wdw', room:'Deluxe Studio · Resort',   name:"Riviera Resort",                  std:[17,19,20,21,24,27,35], pk:[22,24,26,26,28,30,40] },
  { park:'wdw', cal:'wdw', room:'Deluxe Studio · Resort',   name:"Bay Lake Tower",                  std:[13,15,16,17,18,19,26], pk:[16,17,18,19,21,22,29] },
  { park:'wdw', cal:'wdw', room:'Deluxe Studio · Resort',   name:"Villas at Grand Floridian",       std:[16,17,18,18,22,24,32], pk:[20,20,21,21,24,26,37] },
  { park:'wdw', cal:'wdw', room:'Deluxe Studio · Beach',    name:"Beach Club Villas",               std:[14,15,16,16,17,18,27], pk:[15,16,17,18,21,22,28] },
  { park:'wdw', cal:'wdw', room:'Deluxe Studio · Resort',   name:"BoardWalk Villas",                std:[10,10,11,11,14,15,22], pk:[13,14,15,16,16,19,24] },
  { park:'wdw', cal:'wdw', room:'Deluxe Studio',            name:"Copper Creek Villas",             std:[13,15,16,17,17,18,25], pk:[15,16,17,18,19,21,28] },
  { park:'wdw', cal:'wdw', room:'Deluxe Studio',            name:"Boulder Ridge Villas",            std:[13,15,16,16,17,19,26], pk:[16,16,17,18,20,21,28] },
  { park:'wdw', cal:'wdw', room:'Deluxe Studio · Value',    name:"Animal Kingdom Villas",           std:[7,8,8,9,10,12,16],     pk:[10,11,12,12,13,14,17] },
  { park:'wdw', cal:'wdw', room:'Deluxe Studio · Standard', name:"Saratoga Springs",                std:[9,12,13,14,14,15,21],  pk:[14,15,15,16,17,18,25] },
  { park:'wdw', cal:'wdw', room:'Deluxe Studio',            name:"Old Key West",                    std:[9,10,10,11,13,15,22],  pk:[13,14,15,16,17,19,26] },
  { park:'wdw', cal:'wdw', room:'Cabin (sleeps 6)',         name:"Cabins at Fort Wilderness",       std:[15,16,18,20,22,24,32], pk:[18,19,21,24,25,28,36] },
  // ── Disneyland Resort ──
  { park:'dlr', cal:'dlh', room:'Deluxe Studio · Standard', name:"Villas at Disneyland Hotel",      std:[13,15,17,20,23,24,28], pk:[16,18,20,23,26,27,31] },
  { park:'dlr', cal:'gcv', room:'Deluxe Studio',            name:"Villas at Grand Californian",     std:[17,20,26,30],          pk:[22,24,32,37] },
  // ── International concepts (illustrative — no DVC property exists at these parks) ──
  { park:'tds', cal:'wdw', room:'Concept studio',           name:"Fantasy Springs Villas",          std:[16,18,20,22,25,28,36], pk:[20,22,24,26,28,32,40] },
  { park:'dlp', cal:'wdw', room:'Concept studio',           name:"Sequoia Lodge Villas",            std:[12,14,15,16,18,20,27], pk:[15,17,18,19,21,23,30] },
  { park:'shd', cal:'wdw', room:'Concept studio',           name:"Shanghai Lakeside Villas",        std:[11,13,14,15,17,19,26], pk:[14,16,17,18,20,22,29] },
];

/* Travel-period calendars → ordinal day ranges (2026, non-leap). One entry per
   travel period, in chart order; resort point arrays index into these. */
function dvcOrd(m, d){ const days=[0,31,28,31,30,31,30,31,31,30,31,30,31]; let n=d; for(let i=1;i<m;i++) n+=days[i]; return n; }
function dvcR(a, b){ return [dvcOrd(a[0],a[1]), dvcOrd(b[0],b[1])]; }
const DVC_CAL = {
  // Walt Disney World — 7 periods
  wdw:[
    [ dvcR([9,1],[9,30]) ],                                              // TP1
    [ dvcR([1,1],[1,31]), dvcR([5,1],[5,14]) ],                          // TP2
    [ dvcR([5,15],[6,10]), dvcR([12,1],[12,23]) ],                       // TP3
    [ dvcR([2,1],[2,15]), dvcR([6,11],[8,31]) ],                         // TP4
    [ dvcR([10,1],[11,24]), dvcR([11,28],[11,30]) ],                     // TP5
    [ dvcR([2,16],[3,28]), dvcR([4,6],[4,30]), dvcR([11,25],[11,27]) ],  // TP6
    [ dvcR([3,29],[4,5]), dvcR([12,24],[12,31]) ],                       // TP7
  ],
  // Villas at Disneyland Hotel — 7 periods
  dlh:[
    [ dvcR([1,1],[1,31]), dvcR([9,1],[9,15]) ],
    [ dvcR([5,1],[5,22]), dvcR([8,16],[9,15]) ],
    [ dvcR([2,1],[3,14]), dvcR([9,16],[9,30]) ],
    [ dvcR([5,23],[5,31]), dvcR([10,1],[11,23]), dvcR([11,28],[12,17]) ],
    [ dvcR([6,1],[8,15]) ],
    [ dvcR([3,15],[3,28]), dvcR([4,6],[4,30]) ],
    [ dvcR([3,29],[4,5]), dvcR([11,24],[11,27]), dvcR([12,18],[12,31]) ],
  ],
  // Villas at Grand Californian — 4 periods (2026 re-chart)
  gcv:[
    [ dvcR([1,4],[2,21]), dvcR([9,6],[10,1]) ],
    [ dvcR([4,12],[6,28]), dvcR([10,2],[11,23]), dvcR([11,29],[12,17]) ],
    [ dvcR([2,22],[3,26]), dvcR([6,29],[9,5]) ],
    [ dvcR([1,1],[1,3]), dvcR([3,27],[4,11]), dvcR([11,24],[11,28]), dvcR([12,18],[12,31]) ],
  ],
};
function dvcTpIndex(date, calKey){
  const cal = DVC_CAL[calKey]; const o = dvcOrd(date.getMonth()+1, date.getDate());
  for(let i=0;i<cal.length;i++) for(const [s,e] of cal[i]) if(o>=s && o<=e) return i;
  return Math.min(3, cal.length-1); // sensible mid-season default
}

/* nightly breakdown between two yyyy-mm-dd strings */
function dvcNights(inStr, outStr){
  const a = new Date(inStr+'T00:00'), b = new Date(outStr+'T00:00');
  const out = [];
  if(!(a < b)) return out;
  for(let d = new Date(a); d < b; d.setDate(d.getDate()+1)){
    const dow = d.getDay(); // 0 Sun .. 6 Sat
    out.push({ date:new Date(d), peak:(dow===5||dow===6) });
  }
  return out;
}
function dvcStayPoints(resort, ns){
  let total = 0;
  for(const n of ns){ const tp = dvcTpIndex(n.date, resort.cal); total += (n.peak ? resort.pk : resort.std)[tp]; }
  return total;
}
/* how many nights a budget buys, repeating the chosen pattern's avg nightly */
function dvcNightsAffordable(resort, ns, budget){
  if(!ns.length) return 0;
  const avg = dvcStayPoints(resort, ns) / ns.length;
  return Math.floor(budget / avg);
}
function dvcFmtRange(inStr, outStr){
  const opt = { month:'short', day:'numeric' };
  const a = new Date(inStr+'T00:00'), b = new Date(outStr+'T00:00');
  return `${a.toLocaleDateString('en-US',opt)} – ${b.toLocaleDateString('en-US',opt)}, 2026`;
}

/* ─────────────────────────────────────────────────────────────
   PAGE
   ───────────────────────────────────────────────────────────── */
const DVCHelperPage = () => {
  const { useState, useMemo } = React;

  const [tab, setTab] = useState('design');
  const [parks, setParks] = useState(() => new Set(['wdw']));
  const [design, setDesign] = useState({ resort:'', in:'2026-06-12', out:'2026-06-17' });
  const [points, setPoints] = useState({ budget:'150', in:'2026-06-12', out:'2026-06-17' });

  const togglePark = (id) => {
    setParks(prev => {
      const next = new Set(prev);
      if(next.has(id)) next.delete(id); else next.add(id);
      return next;
    });
  };

  const activeResorts = useMemo(() => DVC_RESORTS.filter(r => parks.has(r.park)), [parks]);

  // Keep a focused resort only while its park stays lit.
  const focusResort = activeResorts.some(r => r.name === design.resort) ? design.resort : '';

  const ambientBg = useMemo(() => {
    const layers = [...parks].map(id => {
      const p = DVC_PARK[id];
      return `radial-gradient(closest-side circle at ${p.pos}, rgba(${p.rgb},.16), rgba(${p.rgb},0) 70%)`;
    });
    return layers.length ? layers.join(',') : 'transparent';
  }, [parks]);

  // ── Results ──
  let resultsTitle = tab === 'design' ? 'Estimated stays' : 'Where your points go';
  let resultsMeta = '';
  let body = null;

  const emptyCard = (msg) => (
    <div className="empty glass"><b>No parks lit.</b><br/>{msg}</div>
  );

  if(parks.size === 0){
    resultsTitle = tab === 'design' ? 'Estimated stays' : 'Where your points go';
    body = emptyCard('Light up at least one park above to see estimates.');
  } else if(tab === 'design'){
    const ns = dvcNights(design.in, design.out);
    if(!ns.length){
      body = emptyCard('Pick a check-out date after your check-in.');
    } else {
      let list = activeResorts.map(r => ({ r, total:dvcStayPoints(r, ns) }));
      list.sort((a,b) => a.total - b.total);
      if(focusResort){ const i = list.findIndex(x => x.r.name === focusResort); if(i > 0){ const [f] = list.splice(i,1); list.unshift(f); } }

      resultsMeta = `${list.length} resort${list.length>1?'s':''} · ${dvcFmtRange(design.in, design.out)} · ${ns.length} night${ns.length>1?'s':''}`;
      body = list.map((x, i) => {
        const p = DVC_PARK[x.r.park]; const avg = Math.round(x.total / ns.length);
        const pinned = focusResort && x.r.name === focusResort;
        return (
          <div className="rcard glass" style={{ '--c': p.rgb }} key={x.r.name}>
            <div className="rcard__top">
              <span className="rcard__park">{p.name}</span>
              <span className="rcard__rank">{pinned ? '★ focus' : '#'+(i+1)+' value'}</span>
            </div>
            <h3>{x.r.name}</h3>
            <div className="rcard__pts"><b>{x.total}</b><span>points</span></div>
            <div className="rcard__sub">{x.r.room} · ≈ {avg} pts / night for {ns.length} night{ns.length>1?'s':''}</div>
          </div>
        );
      });
    }
  } else {
    const ns = dvcNights(points.in, points.out);
    const budget = Math.max(0, parseInt(points.budget || 0, 10));
    if(!ns.length){
      body = emptyCard('Pick a check-out date after your check-in.');
    } else {
      let list = activeResorts.map(r => ({ r, total:dvcStayPoints(r, ns) }));
      list.sort((a,b) => a.total - b.total);

      resultsMeta = `${list.length} resort${list.length>1?'s':''} · ${budget} pts · ${dvcFmtRange(points.in, points.out)} · ${ns.length} night${ns.length>1?'s':''}`;
      body = list.map((x, i) => {
        const pk = DVC_PARK[x.r.park];
        const diff = budget - x.total;
        const fits = diff >= 0;
        const maxN = dvcNightsAffordable(x.r, ns, budget);
        const s1 = fits
          ? <span><b>This stay fits.</b> {x.r.name.split(' ')[0]} for your {ns.length} nights costs <b>{x.total} pts</b> — you'd have <b className="pill-ok">{diff} pts to spare.</b></span>
          : <span><b>{x.total} pts</b> for your {ns.length} nights — that's <b className="pill-over">{Math.abs(diff)} pts over.</b> Trim a night or two to fit.</span>;
        const s2 = maxN > 0
          ? <span>Your <b>{budget} pts</b> cover about <b>{maxN} night{maxN>1?'s':''}</b> in a studio here at these dates.</span>
          : <span>A studio here runs hotter than your budget for these dates — try a value resort or off-peak week.</span>;
        return (
          <div className="rcard glass" style={{ '--c': pk.rgb }} key={x.r.name}>
            <div className="rcard__top">
              <span className="rcard__park">{pk.name}</span>
              <span className="rcard__rank">#{i+1} value</span>
            </div>
            <h3>{x.r.name}</h3>
            <div className="rcard__pts"><b>{x.total}</b><span>pts · your dates</span></div>
            <div className="rcard__sub">{x.r.room} · ≈ {Math.round(x.total / ns.length)} pts / night</div>
            <div className="sugg">
              <div className="sugg__row"><span className="sugg__icon">①</span><span className="sugg__txt">{s1}</span></div>
              <div className="sugg__row"><span className="sugg__icon">②</span><span className="sugg__txt">{s2}</span></div>
            </div>
          </div>
        );
      });
    }
  }

  const gridDisplay = (parks.size === 0 || (Array.isArray(body))) ? undefined : 'block';

  return (
    <div className="dvc-helper page__inner fade-in">
      <div className="ambient" style={{ background: ambientBg }} aria-hidden="true"/>

      <header className="hero">
        <div className="eyebrow">DVC Helper · 2026 Points</div>
        <h1 className="display">Plan your points.</h1>
        <p className="lede">Design a stay to see what it costs, or tell us your points and we'll
          find where they take you. Pick the parks you're dreaming about — only lit parks
          are factored into your estimate.</p>
      </header>

      {/* ── Park selector ── */}
      <section className="parks-block">
        <div className="parks-head">
          <div className="eyebrow">Choose your parks</div>
          <div className="parks-hint">Tap to light up · select as many as you like</div>
        </div>
        <div className="parks">
          {DVC_PARKS.map(p => {
            const on = parks.has(p.id);
            return (
              <div
                key={p.id}
                className={'park' + (on ? ' is-on' : '')}
                style={{ '--c': p.rgb }}
                role="button"
                aria-pressed={on}
                onClick={() => togglePark(p.id)}
              >
                <div className="park__check">{on ? '✓' : ''}</div>
                <div className="park__name">{p.name}</div>
                <div className="park__sub">{p.sub}</div>
              </div>
            );
          })}
        </div>
      </section>

      {/* ── Tabs ── */}
      <div className="tabs glass" role="tablist">
        <div className={'tab' + (tab==='design'?' is-active':'')} onClick={() => setTab('design')}>Design A Stay</div>
        <div className={'tab' + (tab==='points'?' is-active':'')} onClick={() => setTab('points')}>Use My Points</div>
      </div>

      {/* ── Design A Stay ── */}
      {tab === 'design' && (
        <section className="panel is-active">
          <div className="glass">
            <div className="form-grid">
              <div className="field">
                <label htmlFor="d-resort">Resort (optional focus)</label>
                <select id="d-resort" value={focusResort} onChange={e => setDesign(d => ({ ...d, resort:e.target.value }))}>
                  <option value="">Any — compare all</option>
                  {activeResorts.map(r => (
                    <option key={r.name} value={r.name}>{r.name} · {DVC_PARK[r.park].name}</option>
                  ))}
                </select>
                <span className="hint">Pinned to the top of your results.</span>
              </div>
              <div className="field">
                <label htmlFor="d-in">Check-in</label>
                <input type="date" id="d-in" value={design.in} min="2026-01-01" max="2026-12-31" onChange={e => setDesign(d => ({ ...d, in:e.target.value }))}/>
              </div>
              <div className="field">
                <label htmlFor="d-out">Check-out</label>
                <input type="date" id="d-out" value={design.out} min="2026-01-02" max="2026-12-31" onChange={e => setDesign(d => ({ ...d, out:e.target.value }))}/>
              </div>
            </div>
          </div>
        </section>
      )}

      {/* ── Use My Points ── */}
      {tab === 'points' && (
        <section className="panel is-active">
          <div className="glass">
            <div className="form-grid">
              <div className="field">
                <label htmlFor="p-budget">My points</label>
                <input type="number" id="p-budget" value={points.budget} min="1" step="1" onChange={e => setPoints(p => ({ ...p, budget:e.target.value }))}/>
                <span className="hint">Annual + banked + borrowed.</span>
              </div>
              <div className="field">
                <label htmlFor="p-in">Check-in</label>
                <input type="date" id="p-in" value={points.in} min="2026-01-01" max="2026-12-31" onChange={e => setPoints(p => ({ ...p, in:e.target.value }))}/>
              </div>
              <div className="field">
                <label htmlFor="p-out">Check-out</label>
                <input type="date" id="p-out" value={points.out} min="2026-01-02" max="2026-12-31" onChange={e => setPoints(p => ({ ...p, out:e.target.value }))}/>
              </div>
            </div>
          </div>
        </section>
      )}

      {/* ── Results ── */}
      <div className="results-head">
        <h2 className="display">{resultsTitle}</h2>
        <div className="results-meta">{resultsMeta}</div>
      </div>
      <div className="results" style={{ display: gridDisplay }}>{body}</div>

      <p className="note">
        Points transcribed from the official 2026 Disney Vacation Club points chart — lowest-view
        Deluxe Studio per night (Animal Kingdom = Value, Saratoga/Disneyland Hotel = Standard,
        Fort Wilderness = Cabin), with weekend nights (Fri–Sat) priced at peak. Each resort uses its
        own travel-period calendar (Grand Californian runs on a 4-season chart). International resorts
        shown are illustrative concepts for planning fun — no DVC property exists at those parks.
        Estimates only; subject to change by DVC.
      </p>
    </div>
  );
};

Object.assign(window, { DVCHelperPage });
