// ════════════════════════════════════════════════════════════════════════
// PropMystro · G6 · pm-d-onsite.jsx  (mirror of pm-maint-mobile.jsx)
// On-site capture — walk the property, photograph issues, raise tickets
// straight onto the maintenance board. Two modes (persisted): Laptop, a wide
// desk-capture form; Phone, a thumb-first phone-frame flow for on-the-spot
// camera captures. Damp & mould auto-escalates to critical with the
// Awaab's-Law response clock. Writes to tickets (source: 'inspection').
// Gated by features.inspect_mobile (Professional). → window.PMOnsite
// ════════════════════════════════════════════════════════════════════════
(function () {
  const { useState, useEffect, useCallback } = React;

  const CATS = [
    { key: 'damp_mould', label: 'Damp & mould', icon: '☁', hazard: true },
    { key: 'plumbing', label: 'Plumbing', icon: '🚿' },
    { key: 'heating', label: 'Heating', icon: '♨' },
    { key: 'electrical', label: 'Electrical', icon: '⚡' },
    { key: 'structural', label: 'Structural', icon: '⌂' },
    { key: 'appliance', label: 'Appliance', icon: '▣' },
    { key: 'pest', label: 'Pests', icon: '🐀' },
    { key: 'compliance', label: 'Compliance', icon: '◑' },
    { key: 'general', label: 'General', icon: '✎' },
  ];
  const PRIORITIES = [{ key: 'low', label: 'Low' }, { key: 'normal', label: 'Normal' }, { key: 'high', label: 'High' }];
  const STAGE_LBL = { inbox: 'Inbox', diagnosing: 'Diagnosing', quoted: 'Quoted', dispatched: 'Dispatched', resolved: 'Resolved' };
  const MODE_KEY = 'pm-onsite-mode';
  const today = () => new Date().toISOString().slice(0, 10);
  function Spin() { return <span className="spin dark" />; }

  // photo slot — camera/file capture persisted per-slot in localStorage;
  // the slot id is stored on the ticket's photos[] (same as the board).
  function PhotoSlot({ id, size, label }) {
    const KEY = 'pm-photo-' + id;
    const [img, setImg] = useState(() => { try { return localStorage.getItem(KEY) || ''; } catch (e) { return ''; } });
    const [over, setOver] = useState(false);
    const take = (file) => {
      if (!file || !/^image\//.test(file.type)) return;
      const r = new FileReader();
      r.onload = e => { try { localStorage.setItem(KEY, e.target.result); } catch (err) {} setImg(e.target.result); };
      r.readAsDataURL(file);
    };
    return <label className={'mt-photo' + (img ? ' filled' : '') + (over ? ' over' : '')} style={{ width: size || 84, height: size || 84 }}
      onDragOver={e => { e.preventDefault(); setOver(true); }}
      onDragLeave={() => setOver(false)}
      onDrop={e => { e.preventDefault(); setOver(false); take(e.dataTransfer.files[0]); }}>
      <input type="file" accept="image/*" capture="environment" style={{ display: 'none' }} onChange={e => { take(e.target.files[0]); e.target.value = ''; }} />
      {img ? <img src={img} alt={label || 'photo'} /> : <span className="mt-photo-seed"><span style={{ fontSize: 18 }}>＋</span><span className="l">{label || 'Photo'}</span></span>}
    </label>;
  }

  function Locked({ openBilling }) {
    return <div className="card card-pad"><div className="locked">
      <div className="lock-ico">◈</div>
      <h3>On-site capture is a Professional feature</h3>
      <p>Walk a property with your phone, photograph issues and raise tickets straight onto the maintenance board — damp &amp; mould auto-escalates with a statutory clock.</p>
      <button className="btn btn-primary btn-sm" style={{ width: 'auto' }} onClick={openBilling}>Upgrade to Professional</button>
      <div className="lock-foot">Your data stays exactly as it is — upgrading just unlocks the module.</div>
    </div></div>;
  }

  function Onsite({ sb, account, toast, openBilling, go }) {
    const entitled = account.features && account.features.inspect_mobile === true;

    const [props, setProps] = useState(null);
    const [propertyId, setPropertyId] = useState('');
    const [recent, setRecent] = useState([]);
    const [draftKey, setDraftKey] = useState(() => Date.now());
    const blank = { title: '', category: 'general', priority: 'normal', description: '' };
    const [f, setF] = useState(blank);
    const [busy, setBusy] = useState(false);
    const [justAdded, setJustAdded] = useState(null);
    const set = (patch) => setF(s => ({ ...s, ...patch }));

    const load = useCallback(async () => {
      if (!entitled) return;
      const { data } = await sb.from('properties').select('id,address,postcode,borough,type,beds').order('address', { ascending: true });
      setProps(data || []);
      setPropertyId(pid => pid || (data && data[0] ? data[0].id : ''));
    }, [sb, entitled]);
    useEffect(() => { load(); }, [load]);

    const loadRecent = useCallback(async () => {
      if (!entitled || !propertyId) return;
      const { data } = await sb.from('tickets').select('id,title,category,priority,status,raised_at').eq('property_id', propertyId).eq('source', 'inspection').order('raised_at', { ascending: false }).limit(6);
      setRecent(data || []);
    }, [sb, propertyId, entitled]);
    useEffect(() => { loadRecent(); }, [loadRecent]);

    if (!entitled) return <React.Fragment>
      <div className="pagehead"><h1>On-site capture</h1><p>Raise tickets from inside the property.</p></div>
      <Locked openBilling={openBilling} />
    </React.Fragment>;
    if (props === null) return <div className="card card-pad"><Spin /></div>;

    const property = props.find(p => p.id === propertyId);
    const cat = CATS.find(c => c.key === f.category) || CATS[CATS.length - 1];
    const isDamp = !!cat.hazard;
    const photoIds = ['onsite-' + draftKey + '-a', 'onsite-' + draftKey + '-b', 'onsite-' + draftKey + '-c'];

    const submit = async () => {
      if (!propertyId || !f.title.trim() || busy) return;
      setBusy(true);
      const { error } = await sb.from('tickets').insert({
        property_id: propertyId, title: f.title.trim(), category: f.category,
        priority: isDamp ? 'critical' : f.priority, status: 'inbox', source: 'inspection',
        raised_by: 'On-site capture', raised_at: today(), description: f.description || null,
        quote_amount: 0, cost: 0, photos: photoIds,
      });
      setBusy(false);
      if (error) { toast(error.message); return; }
      setJustAdded(f.title.trim());
      setF(blank); setDraftKey(Date.now());
      loadRecent();
      setTimeout(() => setJustAdded(null), 3200);
    };

    const shared = { props, propertyId, setPropertyId, property, f, set, cat, isDamp, photoIds, draftKey, submit, busy, justAdded, recent, go };

    return <React.Fragment>
      <OnsiteStyles />
      <div className="pagehead" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 14, flexWrap: 'wrap' }}>
        <div><h1>On-site capture</h1><p>Walk the property, photograph issues, raise tickets straight onto the board. On a phone, each photo slot opens the camera directly.</p></div>
        <button className="btn btn-ghost btn-sm" onClick={() => go && go('maintenance')}>Open the board →</button>
      </div>

      {props.length === 0 ? <div className="card"><div className="empty"><div className="ico">⌂</div><h3>No properties yet</h3><p>Add a property first — then walk it and raise tickets as you go.</p><button className="btn btn-primary btn-sm" onClick={() => go && go('properties')}>Go to Properties</button></div></div>
        : <OnsiteLaptop {...shared} />}
    </React.Fragment>;
  }

  // ── LAPTOP — two-column desk capture ───────────────────────────────────
  function OnsiteLaptop({ props, propertyId, setPropertyId, property, f, set, cat, isDamp, photoIds, draftKey, submit, busy, justAdded, recent }) {
    return <div className="os-laptop">
      <div className="os-top">
        <div className="card card-pad">
          <div className="pmd-mono os-l">PROPERTY</div>
          <select className="pm-input" style={{ width: '100%', marginTop: 6 }} value={propertyId} onChange={e => setPropertyId(e.target.value)}>
            {props.map(p => <option key={p.id} value={p.id}>{p.address}</option>)}
          </select>
          {property && <div className="prop-meta" style={{ marginTop: 10 }}>{[property.postcode, property.borough].filter(Boolean).join(' · ')}{property.type ? ' · ' + property.type.toUpperCase() : ''}{property.beds ? ' · ' + property.beds + ' bed' : ''}</div>}
        </div>
        <div className="card card-pad">
          <div className="pmd-mono os-l">PHOTOS · DRAG, DROP OR CLICK</div>
          <div className="mt-photo-row" style={{ marginTop: 8 }} key={draftKey}>
            {photoIds.map((pid, i) => <PhotoSlot key={pid} id={pid} size={96} label={'Photo ' + (i + 1)} />)}
          </div>
        </div>
      </div>

      <div className="card">
        <div className="card-head"><div><h3>Issue details</h3><div className="sub">Lands in the board's Inbox column instantly.</div></div>
          {justAdded && <span className="badge ok">✓ Raised — “{justAdded}”</span>}</div>
        <div className="card-pad">
          <div className="pmd-mono os-l">CATEGORY</div>
          <div className="os-cats">
            {CATS.map(c => <button key={c.key} className={'os-cat' + (f.category === c.key ? ' on' : '') + (c.hazard ? ' hazard' : '')} onClick={() => set({ category: c.key })}>
              <span className="os-cat-ico">{c.icon}</span><span>{c.label}</span>
            </button>)}
          </div>
          {isDamp && <div className="mt-awaab" style={{ marginTop: 10 }}><strong>Damp &amp; mould</strong>This will be raised as critical with a 14-day response clock (Awaab's Law).</div>}

          <div className="field" style={{ marginTop: 14 }}><label>Summary</label>
            <input value={f.title} placeholder="e.g. Mould on bathroom ceiling" onChange={e => set({ title: e.target.value })} /></div>
          <div className="field"><label>Condition notes</label>
            <textarea className="pm-textarea" rows={3} value={f.description} placeholder="Location, extent, access notes…" onChange={e => set({ description: e.target.value })}></textarea></div>
          {!isDamp && <div className="field"><label>Priority</label>
            <div style={{ display: 'flex', gap: 8 }}>
              {PRIORITIES.map(p => <button key={p.key} className={'os-prio' + (f.priority === p.key ? ' on' : '')} onClick={() => set({ priority: p.key })}>{p.label}</button>)}
            </div></div>}
          <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
            <button className="btn btn-primary btn-sm" style={{ width: 'auto' }} disabled={!f.title.trim() || busy} onClick={submit}>{busy ? 'Raising…' : 'Raise ticket' + (isDamp ? ' · critical' : '')}</button>
          </div>
        </div>
      </div>

      {recent.length > 0 && <div className="card">
        <div className="card-head"><div><h3>Raised on-site at this property</h3></div></div>
        {recent.map(t => { const c = CATS.find(x => x.key === t.category) || {}; return <div className="row" key={t.id}>
          <span className="avatar">{c.icon || '✎'}</span>
          <div className="main"><div className="t">{t.title}</div><div className="s">{c.label || t.category} · {t.priority}</div></div>
          <span className={'badge ' + (t.status === 'resolved' ? 'ok' : 'none')}>{STAGE_LBL[t.status] || t.status}</span>
        </div>; })}
      </div>}
    </div>;
  }

  // ── PHONE — thumb-first phone-frame flow ───────────────────────────────
  function OnsitePhone({ props, propertyId, setPropertyId, property, f, set, cat, isDamp, photoIds, draftKey, submit, busy, justAdded, recent }) {
    const [picking, setPicking] = useState(false);
    return <div className="os-phone-stage">
      <div className="os-phone">
        <div className="os-notch"></div>
        <div className="os-screen">
          <div className="os-status"><span>9:41</span><span>◐ ▮▮▮</span></div>

          <div className="os-ph-top">
            <div className="os-ph-top-l">
              <span className="pmd-mono os-l">INSPECTING</span>
              <strong>{property ? property.address : 'Pick a property'}</strong>
              <span className="pmd-mono os-l">{property ? [property.postcode, property.borough].filter(Boolean).join(' · ') : ''}</span>
            </div>
            <button className="os-switch" onClick={() => setPicking(v => !v)}>{picking ? '✕' : 'Change'}</button>
          </div>

          {picking ? <div className="os-picker">
            {props.map(p => <button key={p.id} className={'os-pick' + (p.id === propertyId ? ' on' : '')} onClick={() => { setPropertyId(p.id); setPicking(false); }}>
              <strong>{p.address}</strong><span className="pmd-mono os-l">{p.postcode}</span>
            </button>)}
          </div> : <div className="os-body">
            {justAdded && <div className="os-toast">✓ Raised — “{justAdded}”. On the board now.</div>}

            <span className="pmd-mono os-l">PHOTOS</span>
            <div className="mt-photo-row" key={draftKey}>
              {photoIds.map((pid, i) => <PhotoSlot key={pid} id={pid} size={70} label={'Photo ' + (i + 1)} />)}
            </div>

            <span className="pmd-mono os-l" style={{ marginTop: 12 }}>WHAT'S THE ISSUE?</span>
            <div className="os-chips">
              {CATS.map(c => <button key={c.key} className={'os-chip' + (f.category === c.key ? ' on' : '') + (c.hazard ? ' hazard' : '')} onClick={() => set({ category: c.key })}>
                <span>{c.icon}</span>{c.label}
              </button>)}
            </div>
            {isDamp && <div className="os-hazard">⚠ Damp &amp; mould — raised critical with a 14-day clock.</div>}

            <span className="pmd-mono os-l" style={{ marginTop: 12 }}>SUMMARY</span>
            <input className="pm-input os-input" value={f.title} placeholder="e.g. Mould on bathroom ceiling" onChange={e => set({ title: e.target.value })} />
            <textarea className="pm-input os-input" rows={3} value={f.description} placeholder="Condition notes, location, access…" onChange={e => set({ description: e.target.value })}></textarea>

            {!isDamp && <div className="os-prios">
              {PRIORITIES.map(p => <button key={p.key} className={'os-prio' + (f.priority === p.key ? ' on' : '')} onClick={() => set({ priority: p.key })}>{p.label}</button>)}
            </div>}

            <button className="os-submit" disabled={!f.title.trim() || busy} onClick={submit}>{busy ? 'Raising…' : 'Raise ticket' + (isDamp ? ' · critical' : '')}</button>

            {recent.length > 0 && <div className="os-recent">
              <span className="pmd-mono os-l">FROM THIS PROPERTY</span>
              {recent.slice(0, 4).map(t => <div key={t.id} className="os-recent-row">
                <span>{(CATS.find(x => x.key === t.category) || {}).icon || '✎'} {t.title}</span>
                <span className="pmd-mono os-l">{STAGE_LBL[t.status] || t.status}</span>
              </div>)}
            </div>}
          </div>}
          <div className="os-home"></div>
        </div>
      </div>

      <div className="os-howto">
        <div className="os-howto-h">Using the phone view</div>
        <ol>
          <li>Tap <strong>Change</strong> to select the property you're standing in.</li>
          <li>Tap each photo slot — on a phone it opens the camera directly.</li>
          <li>Pick the category, add a quick summary, tap <strong>Raise ticket</strong>.</li>
          <li>The ticket lands in the board's <strong>Inbox</strong> column instantly.</li>
        </ol>
        <div className="pmd-mono os-l" style={{ marginTop: 10 }}>Tip: open this page on your actual phone — the layout is the same.</div>
      </div>
    </div>;
  }

  function OnsiteStyles() {
    return <style>{`
      .os-l { font-size: 9.5px; color: var(--ink-faint); letter-spacing: .06em; display: block; }
      .os-laptop { display: flex; flex-direction: column; gap: 16px; max-width: 940px; }
      .os-laptop > .card { margin-top: 0; }
      .os-top { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: stretch; }
      .os-top > .card { margin-top: 0; display: flex; flex-direction: column; }
      .os-cats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; margin-top: 8px; }
      @media (max-width: 600px) { .os-top { grid-template-columns: 1fr; } .os-cats { grid-template-columns: repeat(2, 1fr); } }
      .os-cat { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 13px 6px; border: 1.5px solid var(--line); border-radius: 11px; background: var(--surface); cursor: pointer; font-size: 12px; font-weight: 600; color: var(--ink-soft); transition: border-color .1s, background .1s; min-height: 70px; text-align: center; }
      .os-cat:hover { border-color: var(--ink-faint); background: var(--surface-2); }
      .os-cat.on { border-color: var(--brand-deep); background: var(--brand-soft); color: var(--ink); }
      .os-cat.hazard.on { border-color: var(--red); background: var(--red-soft); }
      .os-cat-ico { font-size: 21px; line-height: 1; }
      .os-prio { border: 1.5px solid var(--line); background: var(--surface); border-radius: 999px; padding: 6px 16px; font-size: 13px; font-weight: 600; color: var(--ink-faint); cursor: pointer; }
      .os-prio.on { border-color: var(--ink); color: var(--ink); background: var(--surface-2); }
      /* phone frame */
      .os-phone-stage { display: flex; justify-content: center; gap: 32px; flex-wrap: wrap; padding-top: 6px; }
      .os-phone { width: 360px; background: #14110d; border-radius: 38px; padding: 10px; box-shadow: 0 18px 50px rgba(26,24,21,.28); position: relative; flex: 0 0 auto; }
      .os-notch { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); width: 120px; height: 22px; background: #14110d; border-radius: 0 0 14px 14px; z-index: 2; }
      .os-screen { background: var(--bg); border-radius: 30px; overflow: hidden; min-height: 620px; display: flex; flex-direction: column; position: relative; }
      .os-status { display: flex; justify-content: space-between; padding: 10px 18px 4px; font-size: 11.5px; font-weight: 600; color: var(--ink-soft); }
      .os-home { width: 110px; height: 4px; border-radius: 3px; background: var(--ink-faint); opacity: .5; margin: 8px auto 8px; }
      .os-ph-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; padding: 10px 16px 12px; border-bottom: 1px solid var(--line); background: var(--surface); }
      .os-ph-top-l { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
      .os-ph-top-l strong { font-size: 14.5px; line-height: 1.25; }
      .os-switch { border: 1.5px solid var(--line); background: var(--surface); border-radius: 999px; padding: 5px 12px; font-size: 12px; font-weight: 700; color: var(--ink-soft); cursor: pointer; flex: 0 0 auto; }
      .os-picker { padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; overflow-y: auto; }
      .os-pick { display: flex; flex-direction: column; gap: 1px; text-align: left; padding: 11px 13px; border: 1.5px solid var(--line); border-radius: 10px; background: var(--surface); cursor: pointer; }
      .os-pick.on { border-color: var(--ink); }
      .os-pick strong { font-size: 13.5px; }
      .os-body { padding: 12px 16px 4px; display: flex; flex-direction: column; gap: 7px; flex: 1; overflow-y: auto; }
      .os-toast { background: var(--ok-soft); color: var(--ok); border-radius: 9px; padding: 9px 12px; font-size: 12.5px; font-weight: 600; }
      .os-chips { display: flex; flex-wrap: wrap; gap: 6px; }
      .os-chip { display: inline-flex; align-items: center; gap: 5px; border: 1.5px solid var(--line); background: var(--surface); border-radius: 999px; padding: 7px 12px; font-size: 12px; font-weight: 600; color: var(--ink-soft); cursor: pointer; min-height: 34px; }
      .os-chip.on { border-color: var(--brand-deep); background: var(--brand-soft); color: var(--ink); }
      .os-chip.hazard.on { border-color: var(--red); background: var(--red-soft); }
      .os-hazard { background: var(--red-soft); color: var(--red); border-radius: 9px; padding: 8px 11px; font-size: 12px; font-weight: 600; }
      .os-input { width: 100%; font-size: 14px; }
      .os-prios { display: flex; gap: 6px; }
      .os-submit { margin-top: 6px; border: 0; background: var(--ink); color: #fff; border-radius: 11px; padding: 14px; font-size: 15px; font-weight: 700; cursor: pointer; min-height: 48px; }
      .os-submit:disabled { background: var(--ink-faint); cursor: default; }
      .os-recent { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; padding-bottom: 8px; }
      .os-recent-row { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; padding: 7px 0; border-bottom: 1px dashed var(--line); }
      .os-howto { max-width: 280px; padding-top: 40px; }
      .os-howto-h { font-weight: 700; font-size: 15px; margin-bottom: 10px; }
      .os-howto ol { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 9px; font-size: 13.5px; color: var(--ink-soft); line-height: 1.45; }
      @media (max-width: 720px) { .os-top { grid-template-columns: 1fr; } .os-howto { display: none; } .os-phone { width: 100%; max-width: 380px; } }
    `}</style>;
  }

  window.PMOnsite = Onsite;
})();
