/* global React, Ic, CloudGate */
const { useState, useEffect, useMemo, useRef } = React;

// =================== LOGIN ===================
function LoginPage({ onLogin }) {
  const [identifier, setIdentifier] = useState('');
  const [pw, setPw] = useState('');
  const [showPw, setShowPw] = useState(false);
  const [error, setError] = useState('');
  const [busy, setBusy] = useState(false);

  const submit = async () => {
    if (busy) return;
    setError(''); setBusy(true);
    try {
      const res = await fetch('/api/auth/login', {
        method:  'POST',
        headers: { 'Content-Type': 'application/json' },
        body:    JSON.stringify({ Identifier: identifier, Password: pw })
      });
      if (res.status === 401) { setError('Email/username or password is incorrect.'); return; }
      if (!res.ok)             { setError('Sign-in failed: ' + res.status);            return; }
      const user = await res.json();
      onLogin(user);
    } catch (err) {
      setError('Network error: ' + err.message);
    } finally {
      setBusy(false);
    }
  };
  return (
    <div className="login-wrap">
      <div className="login-art">
        <div className="login-art-grid"/>
        <div style={{position:'relative', display:'flex', alignItems:'center', gap:12}}>
          <div className="brand-tile" style={{width:36, height:36, fontSize:11}}>RFID</div>
          <div>
            <div style={{fontSize:18, fontWeight:700, color:'white'}}>CloudGate</div>
            <div style={{fontSize:12, color:'oklch(0.75 0.06 285)'}}>Inventory Management</div>
          </div>
          {CloudGate.isDemo() && (
            <div style={{marginLeft:'auto', background:'#F59E0B', color:'#1a1a1a',
                         fontWeight:800, fontSize:12, letterSpacing:'0.08em',
                         padding:'4px 10px', borderRadius:6}}>DEMO</div>
          )}
        </div>
        <div style={{position:'relative', maxWidth:440}}>
          <div style={{fontSize:32, fontWeight:700, lineHeight:1.15, letterSpacing:'-0.02em', color:'white'}}>
            Real-time RFID inventory<br/>across every dock, cooler, and pack line.
          </div>
          <div style={{marginTop:16, fontSize:14, color:'oklch(0.8 0.04 285)', lineHeight:1.55}}>
            One pane of glass for tags, products, locations, and readers — built for multi-site operations.
          </div>
          {/* Sample marketing stats — Demo only; stripped in Production/Test. */}
          {CloudGate.showMarketingUi() && (
          <div style={{marginTop:32, display:'flex', gap:28, fontSize:12, color:'oklch(0.7 0.05 285)'}}>
            <div><span style={{display:'block', fontSize:24, fontWeight:700, color:'white', letterSpacing:'-0.02em'}}>13</span>readers online</div>
            <div><span style={{display:'block', fontSize:24, fontWeight:700, color:'white', letterSpacing:'-0.02em'}}>3,248</span>unique tags</div>
            <div><span style={{display:'block', fontSize:24, fontWeight:700, color:'white', letterSpacing:'-0.02em'}}>8</span>locations</div>
          </div>
          )}
        </div>
        <div style={{position:'relative', fontSize:11.5, color:'oklch(0.65 0.05 285)'}}>© Meiwenti · CloudGate v1.0</div>
      </div>
      <div className="login-form-side">
        <div className="login-card">
          <h1>Sign in</h1>
          <p className="sub">Welcome back. Enter your credentials to continue.</p>
          <div style={{display:'flex', flexDirection:'column', gap:14}}>
            <div className="field">
              <label>Email or username</label>
              <input className="input lg" value={identifier} onChange={e => setIdentifier(e.target.value)}
                     onKeyDown={e => { if (e.key === 'Enter') submit(); }} autoFocus/>
            </div>
            <div className="field">
              <label>Password</label>
              <div style={{position:'relative', display:'flex'}}>
                <input className="input lg" type={showPw ? 'text' : 'password'} value={pw} onChange={e => setPw(e.target.value)}
                       onKeyDown={e => { if (e.key === 'Enter') submit(); }} autoComplete="current-password"
                       style={{flex:1, minWidth:0, paddingRight:40, boxSizing:'border-box'}}/>
                <button type="button" className="icon-btn pw-toggle"
                        title={showPw ? 'Hide password' : 'Show password'}
                        aria-label={showPw ? 'Hide password' : 'Show password'}
                        onClick={() => setShowPw(s => !s)}
                        style={{position:'absolute', right:5, top:'50%', transform:'translateY(-50%)', width:30, height:30}}>
                  {showPw ? <Ic.eyeOff width="18" height="18"/> : <Ic.eye width="18" height="18"/>}
                </button>
              </div>
            </div>
            {error && (
              <div style={{padding:'10px 12px', borderRadius:6, background:'oklch(0.95 0.05 25)',
                            color:'#b91c1c', fontSize:12, fontWeight:500}}>
                {error}
              </div>
            )}
            <button className="btn primary" style={{height:40, justifyContent:'center', marginTop:6}}
                    disabled={busy} onClick={submit}>
              {busy ? 'Signing in…' : <>Sign in <Ic.arrow className="icon"/></>}
            </button>
            <div style={{textAlign:'center', fontSize:12, color:'var(--text-muted)', marginTop:6}}>
              Forgot your password? <a style={{color:'var(--brand)', fontWeight:500}} href="#">Recover</a>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// =================== DASHBOARD ===================
function Dashboard({ setRoute }) {
  // Live values for the active Company (set by the Topbar dropdown).
  // Asset counts come from the aggregate roll-up (CloudGate.AssetCounts) —
  // the full Assets list isn't shipped in the snapshot any more.
  const locations  = CloudGate.byActiveCompany(CloudGate.DB.Locations);
  const readers    = CloudGate.byActiveCompany(CloudGate.DB.Readers);
  const types      = CloudGate.byActiveCompany(CloudGate.DB.AssetTypes);
  // Reader "online" must reflect the REAL situation — live /peer-hub host
  // registration — not the stored ReaderStatusId (only the seeded "Unknown" /
  // operator-set value).  Poll host-status for every reader that has a
  // HardwareId every 5s, exactly like the Readers page.  DB Error / Maintenance
  // still win (operator-flagged); otherwise a reader is online iff its host is
  // currently registered on the hub.
  const [hostStatus, setHostStatus] = useState({});   // readerId → bool (host online)
  const readerSig = readers.map(r => `${r.Id}:${(r.HardwareId || '').trim()}`).join('|');
  useEffect(() => {
    let cancelled = false;
    const poll = async () => {
      const result = {};
      await Promise.all(readers.map(async (r) => {
        const hw = (r.HardwareId || '').trim();
        if (!hw) return;                              // no host binding ⇒ offline
        try {
          const res = await fetch(`/api/remote/host-status?hardwareId=${encodeURIComponent(hw)}`);
          if (!res.ok) return;
          result[r.Id] = !!(await res.json()).Online;
        } catch (_) { /* ignore — treat as offline */ }
      }));
      if (!cancelled) setHostStatus(result);
    };
    poll();
    const t = setInterval(poll, 5_000);
    return () => { cancelled = true; clearInterval(t); };
  }, [readerSig]);   // eslint-disable-line react-hooks/exhaustive-deps

  const readerOnline = (r) => {
    const st = CloudGate.DB.ReaderStatuses.find(s => s.Id === r.ReaderStatusId)?.Status ?? 'Unknown';
    if (st === 'Error' || st === 'Maintenance') return false;   // operator flag wins
    return !!hostStatus[r.Id];
  };
  const onlineRdrs = readers.filter(readerOnline).length;
  const activeId   = CloudGate.DB.AssetStatuses.find(s => s.Status === 'Active')?.Id;
  const totalAsts  = CloudGate.assetCountTotal();
  const activeAsts = activeId != null ? CloudGate.assetCountByStatus(activeId) : totalAsts;

  return (
    <div className="page">
      <div className="page-header">
        <div>
          <h1 className="page-title">Dashboard</h1>
          <p className="page-sub">Overview of locations, readers and assets for the active company.</p>
        </div>
      </div>

      {/* Metric cards — Demo only; stripped in Production/Test. */}
      {CloudGate.showMarketingUi() && (
      <div className="stat-grid">
        <div className="stat">
          <div className="stat-label">Active assets</div>
          <div className="stat-value">{activeAsts.toLocaleString()}</div>
          <div className="stat-meta">{(totalAsts - activeAsts).toLocaleString()} non-active</div>
        </div>
        <div className="stat">
          <div className="stat-label">Locations</div>
          <div className="stat-value">{locations.length}</div>
          <div className="stat-meta">{types.length} asset type(s) defined</div>
        </div>
        <div className="stat">
          <div className="stat-label">Readers online</div>
          <div className="stat-value" style={{color: readers.length === 0 ? undefined : (onlineRdrs === readers.length ? '#16a34a' : '#dc2626')}}>{onlineRdrs} <span style={{fontSize:14, color:'var(--text-muted)', fontWeight:400}}>/ {readers.length}</span></div>
          <div className="stat-meta">{readers.length - onlineRdrs} offline / error / maintenance</div>
        </div>
        <div className="stat">
          <div className="stat-label">Stocking-target breaches</div>
          <div className="stat-value">{locations.filter(l => CloudGate.locationBulletStatus(l.Id) === 'red').length}</div>
          <div className="stat-meta">locations out of range</div>
        </div>
      </div>
      )}

      <div className="card" style={{marginTop:16}}>
        <div className="card-header">
          <h3 className="card-title">Locations</h3>
          <button className="btn ghost sm" onClick={() => setRoute('locations-admin')}>Manage <Ic.chevR className="icon"/></button>
        </div>
        <div className="card-body flush">
          <table className="tbl">
            <thead><tr>
              <th style={{width:24}} title="Stock status"></th>
              <th>Location</th>
              {/* Company — Tenant/System admins only (they span companies). */}
              {CloudGate.isTenantOrSystemAdmin() && <th>Company</th>}
              <th>Type</th>
              <th className="num">Items</th>
              <th className="num">Readers</th>
            </tr></thead>
            <tbody>
              {locations.map(l => {
                const lt        = CloudGate.DB.LocationTypes.find(t => t.Id === l.LocationTypeId);
                const items     = CloudGate.assetCountByLocation(l.Id);
                // Readers at this location shown as online/total: green when all
                // are online, red when any are offline, muted when none exist.
                const locRdrs   = readers.filter(r => r.LocationId === l.Id);
                const rdrTotal  = locRdrs.length;
                const rdrOnline = locRdrs.filter(readerOnline).length;
                const rdrColor  = rdrTotal === 0       ? 'var(--text-muted)'
                                : rdrOnline === rdrTotal ? '#16a34a'
                                :                          '#dc2626';
                const status    = CloudGate.locationBulletStatus(l.Id);
                const dotColor  = status === 'red' ? '#dc2626' : status === 'green' ? '#16a34a' : '#1f2937';
                const dotTitle  = status === 'red'   ? 'At least one asset type is out of range'
                                : status === 'green' ? 'All asset types in range'
                                : 'No stocking targets defined';
                return (
                  <tr key={l.Id}>
                    <td><span title={dotTitle}
                              style={{display:'inline-block', width:10, height:10, borderRadius:'50%', background: dotColor}}/></td>
                    <td><div style={{fontWeight:500}}>{l.Name}</div></td>
                    {CloudGate.isTenantOrSystemAdmin() &&
                      <td style={{color:'var(--text-muted)', fontSize:12.5}}>{(CloudGate.DB.Companies.find(c => c.Id === l.CompanyId)?.Name) || '—'}</td>}
                    <td style={{color:'var(--text-muted)', fontSize:12.5}}>{lt?.LocationTypeName || '—'}</td>
                    <td className="num">{items.toLocaleString()}</td>
                    <td className="num" style={{color: rdrColor, fontWeight:600}}
                        title={`${rdrOnline} of ${rdrTotal} reader(s) online`}>{rdrOnline}/{rdrTotal}</td>
                  </tr>
                );
              })}
              {locations.length === 0 && (
                <tr><td colSpan={CloudGate.isTenantOrSystemAdmin() ? 6 : 5} style={{textAlign:'center', padding:'24px 12px', color:'var(--text-muted)', fontSize:12}}>
                  No locations for this company. Create some on the Locations page.
                </td></tr>
              )}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

function UtilBar({ pct }) {
  const color = pct > 80 ? 'var(--err)' : pct > 60 ? 'var(--warn)' : 'var(--ok)';
  return (
    <div style={{display:'inline-flex', alignItems:'center', gap:8, justifyContent:'flex-end'}}>
      <div style={{width:50, height:4, background:'var(--surface-2)', borderRadius:2, overflow:'hidden'}}>
        <div style={{width: Math.min(100, pct) + '%', height:'100%', background: color}}/>
      </div>
      <span style={{fontVariantNumeric:'tabular-nums', fontSize:12, color:'var(--text-muted)', minWidth:32, textAlign:'right'}}>{pct}%</span>
    </div>
  );
}

function StatusBadge({ status, label }) {
  const map = {
    inventory: { cls:'ok', text:'Inventory' },
    ok:        { cls:'ok', text:'Connected' },
    idle:      { cls:'idle', text:'Idle' },
    warn:      { cls:'warn', text:'Degraded' },
    err:       { cls:'err', text:'Offline' },
  };
  const m = map[status] || map.idle;
  return <span className={"badge-status " + m.cls}><span className="pip"/>{label || m.text}</span>;
}

function ReadsChart() {
  // Simple SVG area chart
  const data = useMemo(() => Array.from({length: 24}, (_, i) => {
    const base = 800 + Math.sin(i/3.2)*400 + Math.cos(i/2.1)*250;
    return Math.max(120, Math.round(base + Math.random()*200));
  }), []);
  const max = Math.max(...data);
  const w = 720, h = 200, pad = 26;
  const xStep = (w - pad*2) / (data.length - 1);
  const points = data.map((v, i) => [pad + i*xStep, h - pad - (v/max)*(h - pad*2)]);
  const path = 'M ' + points.map(p => p.join(',')).join(' L ');
  const area = path + ` L ${w-pad},${h-pad} L ${pad},${h-pad} Z`;
  return (
    <svg viewBox={`0 0 ${w} ${h}`} style={{width:'100%', height:'auto', display:'block'}}>
      <defs>
        <linearGradient id="ag" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="oklch(0.55 0.18 265)" stopOpacity="0.25"/>
          <stop offset="100%" stopColor="oklch(0.55 0.18 265)" stopOpacity="0"/>
        </linearGradient>
      </defs>
      {[0.25, 0.5, 0.75].map(t => (
        <line key={t} x1={pad} x2={w-pad} y1={h-pad - t*(h-pad*2)} y2={h-pad - t*(h-pad*2)} stroke="oklch(0.94 0.005 250)"/>
      ))}
      <path d={area} fill="url(#ag)"/>
      <path d={path} fill="none" stroke="oklch(0.55 0.18 265)" strokeWidth="2"/>
      {points.map((p, i) => i % 4 === 0 && (
        <text key={i} x={p[0]} y={h-8} fontSize="10" fill="oklch(0.5 0.012 250)" textAnchor="middle">{i}h</text>
      ))}
    </svg>
  );
}

// =================== INVENTORY ===================
// Stock matrix for the active company: AssetTypes × Locations.
// Cells show the number of *Active* Assets at that intersection.
//   • green  = count >= Target  (target met or exceeded)
//   • orange = count outside (Min, Max)
//   • plain  = no target, or in the (Min, Target) acceptable range
//   • '—'    = no target and count == 0
function InventoryPage() {
  const [view, setView] = useState('stock');   // 'stock' | 'floor' | 'live'

  const locs     = CloudGate.byActiveCompany(CloudGate.DB.Locations);
  const types    = CloudGate.byActiveCompany(CloudGate.DB.AssetTypes);
  const activeId = CloudGate.DB.AssetStatuses.find(s => s.Status === 'Active')?.Id;
  const qtys     = CloudGate.DB.LocationAssetQuantities;
  // Counts come from the AssetCounts aggregate — the full row list isn't
  // shipped in the snapshot any more.  `activeId ?? null` means "any status"
  // when the lookup is missing.
  const statusFilter = activeId ?? null;
  const cellCount    = (locId, typeId) => CloudGate.assetCountAt(locId, typeId, statusFilter);
  const targetFor    = (locId, typeId) => qtys.find(q => q.LocationId === locId && q.AssetTypeId === typeId);
  const rowTotal     = (typeId) => CloudGate.assetCountByAssetType(typeId, statusFilter);
  const colTotal     = (locId)  => CloudGate.assetCountByLocation (locId,  statusFilter);
  const grandTotal   = statusFilter != null ? CloudGate.assetCountByStatus(statusFilter) : CloudGate.assetCountTotal();

  // Aggregated Min/Max — summed across the same axis as the corresponding
  // total, so the row total can be compared against the row's combined
  // Min/Max and likewise for the column total.  `has` tells the renderer
  // whether any target row exists at all (no targets → no badge, no colour).
  const rowMinMax = (typeId) => {
    let min = 0, max = 0, has = false;
    for (const l of locs) {
      const q = targetFor(l.Id, typeId);
      if (q) { min += q.Minimum; max += q.Maximum; has = true; }
    }
    return { min, max, has };
  };
  const colMinMax = (locId) => {
    let min = 0, max = 0, has = false;
    for (const t of types) {
      const q = targetFor(locId, t.Id);
      if (q) { min += q.Minimum; max += q.Maximum; has = true; }
    }
    return { min, max, has };
  };

  // Cell colour: traffic-light against the (Min, Target, Max) range.
  //   red    — count below Minimum  (underflow)
  //   orange — count above Maximum  (overflow)
  //   green  — count at or above Target (still within Max)
  //   none   — between Min and Target
  const cellStyle = (count, tgt) => {
    if (!tgt) return null;
    if (count < tgt.Minimum)  return { background:'#dc2626', color:'#fff', fontWeight:700 };  // red
    if (count > tgt.Maximum)  return { background:'#ea580c', color:'#fff', fontWeight:700 };  // orange
    if (count >= tgt.Target)  return { background:'#16a34a', color:'#fff', fontWeight:700 };  // green
    return null;
  };
  // Totals don't have a Target — anything inside [Min, Max] is healthy and
  // shown green; below Min is red (underflow), above Max is orange (overflow).
  const totalStyle = (count, mm) => {
    if (!mm.has) return null;
    if (count < mm.min) return { background:'#dc2626', color:'#fff', fontWeight:700 };
    if (count > mm.max) return { background:'#ea580c', color:'#fff', fontWeight:700 };
    return { background:'#16a34a', color:'#fff', fontWeight:700 };
  };

  return (
    <div className="page">
      <div className="page-header">
        <div>
          <h1 className="page-title">Inventory</h1>
          <p className="page-sub">Stock by location, floor plan, and live tag stream.</p>
        </div>
        <div className="page-actions">
          <div className="seg">
            <button className={view==='stock' ? 'active' : ''} onClick={() => setView('stock')}>Stock grid</button>
            <button className={view==='floor' ? 'active' : ''} onClick={() => setView('floor')}>Floor plan</button>
            <button className={view==='live'  ? 'active' : ''} onClick={() => setView('live')}>Live stream</button>
          </div>
          <button className="btn"><Ic.download className="icon"/>Export</button>
        </div>
      </div>

      {view !== 'stock' && (
        <div className="card" style={{padding:48, textAlign:'center', color:'var(--text-muted)', fontSize:13}}>
          {view === 'floor'
            ? 'Floor plan view — coming soon. Will visualise asset positions on a map of the active location.'
            : 'Live stream view — coming soon. Will tail RFID reads from your readers in real time.'}
        </div>
      )}

      {view === 'stock' && (
        <div className="card">
          <div style={{overflowX:'auto'}}>
            <table className="tbl feature">
              <thead>
                <tr>
                  <th style={{textTransform:'uppercase', letterSpacing:'.04em', fontSize:11}}>Asset type</th>
                  {locs.map(l => (
                    <th key={l.Id} className="num" style={{whiteSpace:'nowrap'}}>{l.Name}</th>
                  ))}
                  <th className="num" style={{borderLeft:'1px solid var(--border)', textTransform:'uppercase', letterSpacing:'.04em', fontSize:11}}>Total</th>
                </tr>
              </thead>
              <tbody>
                {types.map(t => (
                  <tr key={t.Id}>
                    <td style={{fontWeight:600}}>{t.AssetTypeName}</td>
                    {locs.map(l => {
                      const c    = cellCount(l.Id, t.Id);
                      const tgt  = targetFor(l.Id, t.Id);
                      const style = cellStyle(c, tgt) || {};
                      const title = tgt
                        ? `Target ${tgt.Target} · Min ${tgt.Minimum} · Max ${tgt.Maximum}`
                        : 'No stocking target';
                      const display = (c === 0 && !tgt)
                        ? <span style={{color:'var(--text-faint)'}}>—</span>
                        : c;
                      return (
                        <td key={l.Id} className="num" title={title} style={style}>
                          {tgt && (
                            <div style={{fontSize:10, fontWeight:500, opacity:0.85, lineHeight:1.1, marginBottom:1}}>
                              {tgt.Minimum}–{tgt.Maximum}
                            </div>
                          )}
                          <div>{display}</div>
                        </td>
                      );
                    })}
                    {(() => {
                      const total = rowTotal(t.Id);
                      const mm    = rowMinMax(t.Id);
                      const style = { ...(totalStyle(total, mm) || { fontWeight:700, color:'var(--text-muted)' }), borderLeft:'1px solid var(--border)' };
                      const title = mm.has ? `Min ${mm.min} · Max ${mm.max}` : '';
                      return (
                        <td className="num" title={title} style={style}>
                          {mm.has && (
                            <div style={{fontSize:10, fontWeight:500, opacity:0.85, lineHeight:1.1, marginBottom:1}}>
                              {mm.min}–{mm.max}
                            </div>
                          )}
                          <div>{total.toLocaleString()}</div>
                        </td>
                      );
                    })()}
                  </tr>
                ))}
                {types.length > 0 && locs.length > 0 && (
                  <tr style={{borderTop:'2px solid var(--border)'}}>
                    <td style={{textTransform:'uppercase', letterSpacing:'.04em', fontSize:11, fontWeight:700, color:'var(--text-muted)'}}>
                      Location total
                    </td>
                    {locs.map(l => {
                      const total = colTotal(l.Id);
                      const mm    = colMinMax(l.Id);
                      const style = totalStyle(total, mm) || { fontWeight:700, color:'var(--text-muted)' };
                      const title = mm.has ? `Min ${mm.min} · Max ${mm.max}` : '';
                      return (
                        <td key={l.Id} className="num" title={title} style={style}>
                          {mm.has && (
                            <div style={{fontSize:10, fontWeight:500, opacity:0.85, lineHeight:1.1, marginBottom:1}}>
                              {mm.min}–{mm.max}
                            </div>
                          )}
                          <div>{total.toLocaleString()}</div>
                        </td>
                      );
                    })}
                    <td className="num" style={{fontWeight:800, borderLeft:'1px solid var(--border)', background:'#bbf7d0', color:'#14532d'}}>
                      {grandTotal.toLocaleString()}
                    </td>
                  </tr>
                )}
                {(types.length === 0 || locs.length === 0) && (
                  <tr><td colSpan={locs.length + 2} style={{textAlign:'center', padding:'24px 12px', color:'var(--text-muted)', fontSize:12}}>
                    {types.length === 0
                      ? 'No asset types for this company. Create some on the Asset types page.'
                      : 'No locations for this company. Create some on the Locations page.'}
                  </td></tr>
                )}
              </tbody>
            </table>
          </div>
        </div>
      )}
    </div>
  );
}

window.LoginPage = LoginPage;
window.Dashboard = Dashboard;
window.StatusBadge = StatusBadge;
window.InventoryPage = InventoryPage;
