/* Roundhouse — Gun Story.
   The differentiator: every gun accumulates a character + a timeline + milestones.
   All computed live from real data (the gun + its sessions). No placeholders.

   Exports (window):
     computeGunStory(gun, sessions) -> story object
     GunStoryCard   — the "character card" (hero stats + generated character line)
     GunTimeline    — SVG cumulative-rounds life arc with milestone markers
     GunMilestones  — milestone list (auto-generated)
*/

// ── Data helpers ────────────────────────────────────────────────────────

// Rounds fired for a specific gun within one session. Defensive about the
// few shapes sessions get saved in across Plan / Quick Log / Voice.
function rhRoundsForGun(session, gunId) {
  if (!session || !session.guns) return 0;
  var entry = session.guns.find(function (g) {
    return g.gunId === gunId || g.id === gunId;
  });
  if (!entry) return 0;
  if (entry.ammos && entry.ammos.length) {
    return entry.ammos.reduce(function (s, a) { return s + (parseInt(a.rounds, 10) || 0); }, 0);
  }
  return parseInt(entry.rounds, 10) || 0;
}

function rhSessionHasGun(session, gunId) {
  return (session.guns || []).some(function (g) {
    return g.gunId === gunId || g.id === gunId;
  });
}

function rhDaysBetween(a, b) {
  var ms = new Date(b).getTime() - new Date(a).getTime();
  return Math.max(0, Math.floor(ms / 86400000));
}

function rhShortDate(iso) {
  if (!iso) return '—';
  var d = new Date(iso);
  var mo = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][d.getMonth()];
  var yr = d.getFullYear();
  var now = new Date();
  return mo + ' ' + d.getDate() + (yr !== now.getFullYear() ? " '" + String(yr).slice(2) : '');
}

// ── The story engine ────────────────────────────────────────────────────

function computeGunStory(gun, allSessions) {
  var gunId   = gun.id;
  var acquired = gun.createdAt || (gun.acquiredDate ? new Date(gun.acquiredDate).toISOString() : null);

  var mine = (allSessions || [])
    .filter(function (s) { return rhSessionHasGun(s, gunId); })
    .map(function (s) {
      return {
        date:     s.date,
        rounds:   rhRoundsForGun(s, gunId),
        malf:     (s.malfunctions || []).length,
        location: s.location || '',
        raw:      s,
      };
    })
    .sort(function (a, b) { return new Date(a.date) - new Date(b.date); });

  // Cumulative life arc
  var cum = 0;
  var series = mine.map(function (p) {
    cum += p.rounds;
    return { date: p.date, rounds: p.rounds, cum: cum, malf: p.malf };
  });

  var sessionRoundTotal = cum;
  var lifetimeRounds    = gun.startingRounds || sessionRoundTotal || 0;
  var sessionCount      = mine.length;
  var malfTotal         = mine.reduce(function (s, p) { return s + p.malf; }, 0);
  var firstTrip         = mine.length ? mine[0].date : null;
  var lastTrip          = mine.length ? mine[mine.length - 1].date : null;
  var daysOwned         = acquired ? rhDaysBetween(acquired, new Date().toISOString()) : null;
  var bestSession       = mine.reduce(function (best, p) { return p.rounds > best.rounds ? p : best; }, { rounds: 0 });

  // Reliability — the signature stat
  var reliability = malfTotal === 0
    ? '100%'
    : (Math.max(0, (1 - malfTotal / Math.max(lifetimeRounds, 1)) * 100)).toFixed(2) + '%';
  var roundsPerMalf = malfTotal === 0 ? null : Math.round(lifetimeRounds / malfTotal);

  // ── Milestones (auto-generated, dated from real crossings) ──
  var milestones = [];
  if (acquired) milestones.push({ date: acquired, label: 'Acquired', kind: 'acquired' });
  if (firstTrip) milestones.push({ date: firstTrip, label: 'First range trip', kind: 'trip' });

  [100, 500, 1000, 2500, 5000, 10000].forEach(function (T) {
    var hit = series.find(function (p) { return p.cum >= T; });
    if (hit) milestones.push({ date: hit.date, label: T.toLocaleString() + ' rounds', kind: 'threshold', value: T });
  });

  if (bestSession.rounds > 0 && sessionCount > 1) {
    milestones.push({ date: bestSession.date, label: 'Personal best · ' + bestSession.rounds.toLocaleString() + ' in a day', kind: 'record' });
  }

  // Clean streak — consecutive recent sessions with zero malfunctions
  var streak = 0;
  for (var i = mine.length - 1; i >= 0; i--) {
    if (mine[i].malf === 0) streak++; else break;
  }
  if (streak >= 5) {
    milestones.push({ date: lastTrip, label: streak + '-session clean streak', kind: 'clean' });
  }

  // Modifications with dates (from config)
  var mods = (gun.config && gun.config.modifications) || [];
  mods.forEach(function (m) {
    if (m && m.date) milestones.push({ date: new Date(m.date).toISOString(), label: 'Mod · ' + (m.name || m.part || 'installed'), kind: 'mod' });
  });

  milestones.sort(function (a, b) { return new Date(b.date) - new Date(a.date); }); // newest first

  // ── Character line — the emotional hook, generated from data ──
  var characterLine;
  if (lifetimeRounds === 0) {
    characterLine = 'Freshly acquired. No rounds downrange yet — its story starts at the range.';
  } else {
    var parts = [];
    if (daysOwned != null) parts.push(daysOwned.toLocaleString() + ' day' + (daysOwned === 1 ? '' : 's') + ' owned');
    parts.push(lifetimeRounds.toLocaleString() + ' rounds downrange');
    var tail = malfTotal === 0
      ? 'Never a malfunction.'
      : (roundsPerMalf ? 'One stoppage every ' + roundsPerMalf.toLocaleString() + ' rounds.' : '');
    characterLine = parts.join(' · ') + '. ' + tail;
  }

  return {
    gunId: gunId,
    acquired: acquired,
    lifetimeRounds: lifetimeRounds,
    sessionCount: sessionCount,
    malfTotal: malfTotal,
    reliability: reliability,
    roundsPerMalf: roundsPerMalf,
    firstTrip: firstTrip,
    lastTrip: lastTrip,
    daysOwned: daysOwned,
    bestSession: bestSession,
    cleanStreak: streak,
    series: series,
    milestones: milestones,
    characterLine: characterLine,
  };
}

// ── The character card ──────────────────────────────────────────────────

function GunStoryCard({ gun, story }) {
  var name = gun.name || [gun.manufacturer, gun.model].filter(Boolean).join(' ') || 'Firearm';
  var reliable = story.malfTotal === 0;

  return (
    <div style={{
      background: 'linear-gradient(160deg, var(--rh-obsidian-800) 0%, var(--rh-obsidian-900) 100%)',
      border: '1px solid var(--rh-brass-tint-32)',
      borderRadius: 4,
      padding: '18px 18px 16px',
      position: 'relative',
      overflow: 'hidden',
    }}>
      {/* faint brass corner glow */}
      <div style={{position:'absolute', top:-40, right:-40, width:120, height:120, borderRadius:'50%', background:'radial-gradient(circle, rgba(184,154,86,0.10) 0%, transparent 70%)', pointerEvents:'none'}}/>

      <div className="label-md" style={{color:'var(--rh-brass-400)', fontSize:11, letterSpacing:'0.24em'}}>
        {gun.caliber || '—'}{story.acquired ? ' · SINCE ' + rhShortDate(story.acquired).toUpperCase() : ''}
      </div>
      <div style={{fontFamily:'var(--font-display)', fontSize:30, color:'var(--fg-1)', letterSpacing:'0.02em', lineHeight:1, marginTop:4}}>{name}</div>

      {/* Hero number — lifetime rounds */}
      <div style={{display:'flex', alignItems:'flex-end', gap:10, marginTop:16}}>
        <div style={{fontFamily:'var(--font-display)', fontSize:52, color:'var(--rh-brass-200)', letterSpacing:'0.01em', lineHeight:0.85, fontVariantNumeric:'tabular-nums'}}>
          {story.lifetimeRounds.toLocaleString()}
        </div>
        <div className="label-md" style={{color:'var(--rh-bone-500)', fontSize:11, marginBottom:4, letterSpacing:'0.20em'}}>ROUNDS<br/>DOWNRANGE</div>
      </div>

      {/* Signature stat row */}
      <div style={{display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:0, marginTop:16, borderTop:'1px solid var(--border-hairline)', paddingTop:12}}>
        <StoryStat v={story.reliability} k="Reliability" tone={reliable ? 'var(--rh-green)' : 'var(--fg-1)'}/>
        <StoryStat v={String(story.sessionCount)} k="Range trips"/>
        <StoryStat v={story.daysOwned != null ? story.daysOwned.toLocaleString() : '—'} k="Days owned" last/>
      </div>

      {/* Character line — the hook */}
      <div style={{
        marginTop:14, paddingTop:12, borderTop:'1px solid var(--border-hairline)',
        fontFamily:'var(--font-body)', fontSize:13, lineHeight:1.5, color:'var(--rh-bone-300)', fontStyle:'italic',
      }}>
        {story.characterLine}
      </div>
    </div>
  );
}

function StoryStat({ v, k, tone, last }) {
  return (
    <div style={{padding:'0 12px', borderRight: last ? 'none' : '1px solid var(--border-hairline)'}}>
      <Metric v={v} k={k} tone={tone}/>
    </div>
  );
}

// ── The timeline — cumulative rounds life arc with milestone markers ─────

function GunTimeline({ story }) {
  var series = story.series || [];

  if (series.length < 2) {
    return (
      <EmptyState title="The arc starts after your second trip" body="Log a few range sessions and this gun's life arc fills in here."/>
    );
  }

  var W = 320, H = 132, pad = 14, padB = 24;
  var t0 = new Date(story.acquired || series[0].date).getTime();
  var t1 = new Date(series[series.length - 1].date).getTime();
  if (t1 <= t0) t1 = t0 + 86400000;
  var maxCum = series[series.length - 1].cum || 1;

  function x(date) { return pad + ((new Date(date).getTime() - t0) / (t1 - t0)) * (W - 2 * pad); }
  function y(cum)  { return (H - padB) - (cum / maxCum) * (H - pad - padB); }

  // Build line + area paths
  var pts = series.map(function (p) { return { px: x(p.date), py: y(p.cum), malf: p.malf, p: p }; });
  // include acquisition as the origin point at cum 0
  var originX = x(story.acquired || series[0].date);
  var baseline = H - padB;

  var linePath = 'M ' + originX + ' ' + baseline + ' ' +
    pts.map(function (q) { return 'L ' + q.px.toFixed(1) + ' ' + q.py.toFixed(1); }).join(' ');
  var areaPath = linePath + ' L ' + pts[pts.length - 1].px.toFixed(1) + ' ' + baseline + ' L ' + originX + ' ' + baseline + ' Z';

  // Milestone markers (thresholds + acquired + record) plotted on the curve
  var markers = (story.milestones || []).filter(function (m) {
    return m.kind === 'threshold' || m.kind === 'record' || m.kind === 'acquired';
  }).map(function (m) {
    // find cum at this milestone's date
    var atOrBefore = series.filter(function (p) { return new Date(p.date) <= new Date(m.date); });
    var cumAt = atOrBefore.length ? atOrBefore[atOrBefore.length - 1].cum : 0;
    return { mx: x(m.date), my: m.kind === 'acquired' ? baseline : y(cumAt), kind: m.kind };
  });

  return (
    <div className="card" style={{padding:'12px 12px 8px'}}>
      <svg viewBox={'0 0 ' + W + ' ' + H} style={{width:'100%', height:'auto', display:'block'}}>
        <defs>
          <linearGradient id="rh-arc" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="rgba(184,154,86,0.34)"/>
            <stop offset="100%" stopColor="rgba(184,154,86,0.02)"/>
          </linearGradient>
        </defs>

        {/* baseline */}
        <line x1={pad} y1={baseline} x2={W - pad} y2={baseline} stroke="rgba(230,225,214,0.10)" strokeWidth="1"/>

        {/* area + line */}
        <path d={areaPath} fill="url(#rh-arc)"/>
        <path d={linePath} fill="none" stroke="var(--rh-brass-400)" strokeWidth="1.6" strokeLinejoin="round"/>

        {/* malfunction markers — drawn first so milestone dots sit on top.
            A red line drops from the curve to the baseline + a diamond on the
            curve, so a stoppage is unmistakable against the brass arc. */}
        {pts.filter(function (q) { return q.malf > 0; }).map(function (q, i) {
          return (
            <g key={'m' + i}>
              <line x1={q.px} y1={q.py} x2={q.px} y2={baseline + 7} stroke="var(--rh-dusty-red)" strokeWidth="1.4" strokeDasharray="2 2"/>
              <rect x={q.px - 3.2} y={q.py - 3.2} width="6.4" height="6.4" transform={'rotate(45 ' + q.px + ' ' + q.py + ')'}
                fill="var(--rh-dusty-red)" stroke="var(--rh-obsidian-900)" strokeWidth="1"/>
            </g>
          );
        })}

        {/* milestone markers */}
        {markers.map(function (mk, i) {
          return <circle key={i} cx={mk.mx} cy={mk.my} r={mk.kind === 'record' ? 3.2 : 2.6}
            fill={mk.kind === 'record' ? 'var(--rh-bone-100)' : 'var(--rh-brass-200)'}
            stroke="var(--rh-obsidian-900)" strokeWidth="1"/>;
        })}

        {/* end-cap label */}
        <text x={W - pad} y={y(maxCum) - 6} textAnchor="end"
          fontFamily="var(--font-display)" fontSize="13" fill="var(--rh-brass-200)"
          style={{letterSpacing:'0.02em'}}>{maxCum.toLocaleString()}</text>
      </svg>

      <div style={{display:'flex', justifyContent:'space-between', marginTop:4, fontFamily:'var(--font-ui)', fontSize:11, letterSpacing:'0.18em', fontWeight:600, color:'var(--rh-bone-500)'}}>
        <span>{rhShortDate(story.acquired || series[0].date).toUpperCase()}</span>
        <span style={{display:'flex', alignItems:'center', gap:6}}>
          <span style={{width:6, height:6, borderRadius:'50%', background:'var(--rh-brass-200)', display:'inline-block'}}/> MILESTONE
          <span style={{width:8, height:2, background:'var(--rh-dusty-red)', display:'inline-block', marginLeft:6}}/> MALF
        </span>
        <span>TODAY</span>
      </div>
    </div>
  );
}

// ── Milestone list ───────────────────────────────────────────────────────

function GunMilestones({ story }) {
  var ms = story.milestones || [];
  if (!ms.length) {
    return (
      <EmptyState title="No milestones yet" body="Milestones appear as you log rounds and sessions."/>
    );
  }
  var glyph = { acquired:'●', trip:'▸', threshold:'◆', record:'★', clean:'✦', mod:'⚙' };
  return (
    <div className="card" style={{padding:'4px 14px'}}>
      {ms.map(function (m, i) {
        return (
          <div key={i} style={{display:'grid', gridTemplateColumns:'18px 64px 1fr', gap:10, alignItems:'center', padding:'13px 0', borderBottom: i < ms.length - 1 ? '1px solid var(--border-hairline)' : 'none'}}>
            <span style={{color: m.kind === 'record' ? 'var(--rh-bone-100)' : 'var(--rh-brass-400)', fontSize:13, textAlign:'center', lineHeight:1}}>{glyph[m.kind] || '◆'}</span>
            <span className="label-md" style={{color:'var(--rh-bone-500)'}}>{rhShortDate(m.date)}</span>
            <span style={{fontFamily:'var(--font-ui)', fontSize:14, color:'var(--fg-1)', textTransform:'uppercase', letterSpacing:'0.05em', fontWeight:600}}>{m.label}</span>
          </div>
        );
      })}
    </div>
  );
}

// ── Reliability heatmap — ammo brand × gun, intensity = malfunction count ─

function rhGunDisplayName(g) {
  return g.name || [g.manufacturer, g.model].filter(Boolean).join(' ') || 'Firearm';
}

function ReliabilityHeatmap({ malfunctions, guns }) {
  var malfs = malfunctions || [];

  if (malfs.length === 0) {
    return (
      <EmptyState tone="positive" title="Zero malfunctions" body="Log a malfunction during a live session and this grid maps which ammo brands cause issues in which guns — so you can spot the bad combos."/>
    );
  }

  // Rows = ammo brands
  var brands = [];
  malfs.forEach(function (m) {
    var b = m.brand || 'Unknown';
    if (brands.indexOf(b) < 0) brands.push(b);
  });

  // Columns = all owned guns (so clean guns show as all-zero columns — the insight)
  var gunCols = [];
  (guns || []).forEach(function (g) {
    var n = rhGunDisplayName(g);
    if (!gunCols.find(function (c) { return c.name === n; })) gunCols.push({ name: n });
  });
  // include any malfunction's gun that isn't in owned guns (e.g. since-deleted)
  malfs.forEach(function (m) {
    if (m.gunName && !gunCols.find(function (c) { return c.name === m.gunName; })) {
      gunCols.push({ name: m.gunName });
    }
  });

  function countFor(brand, gunName) {
    return malfs.filter(function (m) {
      return (m.brand || 'Unknown') === brand && m.gunName === gunName;
    }).length;
  }

  var maxCount = 1;
  brands.forEach(function (b) {
    gunCols.forEach(function (c) { maxCount = Math.max(maxCount, countFor(b, c.name)); });
  });

  function abbrev(name) {
    return name.length > 9 ? name.slice(0, 8) + '…' : name;
  }

  var gridCols = '92px repeat(' + gunCols.length + ', minmax(40px, 1fr))';

  return (
    <div className="card" style={{padding:'12px', overflowX:'auto'}}>
      <div style={{minWidth: gunCols.length > 3 ? (92 + gunCols.length * 48) : 'auto'}}>
        {/* Header row — gun names */}
        <div style={{display:'grid', gridTemplateColumns: gridCols, gap:3, marginBottom:3}}>
          <div className="label-md" style={{fontSize:10, color:'var(--rh-bone-500)', alignSelf:'end', letterSpacing:'0.12em'}}>AMMO ↓ / GUN →</div>
          {gunCols.map(function (c) {
            return (
              <div key={c.name} title={c.name} style={{
                fontFamily:'var(--font-ui)', fontSize:10, letterSpacing:'0.08em', fontWeight:600,
                color:'var(--rh-bone-300)', textTransform:'uppercase', textAlign:'center',
                alignSelf:'end', lineHeight:1.1, paddingBottom:2,
              }}>{abbrev(c.name)}</div>
            );
          })}
        </div>

        {/* Brand rows */}
        {brands.map(function (b) {
          return (
            <div key={b} style={{display:'grid', gridTemplateColumns: gridCols, gap:3, marginBottom:3}}>
              <div title={b} style={{
                fontFamily:'var(--font-ui)', fontSize:10, letterSpacing:'0.04em', fontWeight:600,
                color:'var(--fg-2)', textTransform:'uppercase', alignSelf:'center',
                overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap',
              }}>{b}</div>
              {gunCols.map(function (c) {
                var n = countFor(b, c.name);
                var alpha = n === 0 ? 0 : 0.18 + (n / maxCount) * 0.62;
                return (
                  <div key={c.name} title={b + ' × ' + c.name + ' · ' + n + ' malfunction' + (n === 1 ? '' : 's')} style={{
                    aspectRatio:'1.4',
                    minHeight:30,
                    background: n === 0 ? 'var(--rh-obsidian-700)' : 'rgba(195,58,46,' + alpha.toFixed(2) + ')',
                    border: n === 0 ? '1px solid var(--border-hairline)' : '1px solid rgba(195,58,46,0.5)',
                    borderRadius:2,
                    display:'flex', alignItems:'center', justifyContent:'center',
                    fontFamily:'var(--font-display)', fontSize:14, letterSpacing:'0.02em',
                    color: n === 0 ? 'var(--rh-bone-700)' : (alpha > 0.5 ? 'var(--fg-1)' : 'var(--rh-red-soft)'),
                  }}>{n === 0 ? '·' : n}</div>
                );
              })}
            </div>
          );
        })}

        {/* Legend */}
        <div style={{display:'flex', alignItems:'center', justifyContent:'flex-end', gap:5, marginTop:8, fontFamily:'var(--font-ui)', fontSize:10, letterSpacing:'0.18em', fontWeight:600, color:'var(--rh-bone-500)'}}>
          <span>FEWER</span>
          {[0.2, 0.4, 0.62, 0.8].map(function (o, i) {
            return <span key={i} style={{width:9, height:9, background:'rgba(195,58,46,' + o + ')', display:'inline-block', borderRadius:1}}/>;
          })}
          <span>MORE MALFUNCTIONS</span>
        </div>
      </div>
    </div>
  );
}

window.computeGunStory = computeGunStory;
window.GunStoryCard   = GunStoryCard;
window.GunTimeline    = GunTimeline;
window.GunMilestones  = GunMilestones;
window.ReliabilityHeatmap = ReliabilityHeatmap;
window.rhRoundsForGun = rhRoundsForGun;
window.rhSessionHasGun = rhSessionHasGun;
window.rhGunDisplayName = rhGunDisplayName;
window.rhShortDate    = rhShortDate;
