// MaxAMove — Estimate Preview & Invoice Components

const { useState, useRef } = React;

function calcTotals(pricing, moveData) {
  const distance = parseFloat(moveData.distance) || 0;
  const hours = parseFloat(pricing.hours) || 0;
  const crewRate = parseFloat(pricing.hourlyRate) || 0;
  const laborTotal = hours * crewRate;
  const truckTotal = parseFloat(pricing.truckFee) || 0;
  const fuelTotal = distance * (parseFloat(pricing.fuelRate) || 0);
  const insuranceTotal = pricing.insuranceType === 'flat'
    ? (parseFloat(pricing.insuranceFee) || 0)
    : (parseFloat(pricing.insurancePct) || 0) / 100 * (parseFloat(pricing.insuranceValue) || 0);
  const packingTotal = parseFloat(pricing.packingFee) || 0;
  const baseSubtotal = laborTotal + truckTotal + fuelTotal + insuranceTotal + packingTotal;

  // Add-on surcharges (each: { id, name, qty, price, type, unit })
  // Trip Fee is filtered out of the user-defined surcharges since it's a first-class
  // line item (pricing.tripFee) — including it both ways would double-count.
  const userSurcharges = (Array.isArray(pricing.surcharges) ? pricing.surcharges : [])
    .filter(s => !((s.name || '').trim().toLowerCase() === 'trip fee'));
  const surchargeLines = userSurcharges.map(s => {
    const qty = parseFloat(s.qty) || 1;
    const price = parseFloat(s.price) || 0;
    const amount = s.type === 'percent' ? (baseSubtotal * price / 100) : qty * price;
    const label = s.type === 'percent'
      ? `${s.name} (${price}% surcharge)`
      : (qty > 1 ? `${s.name} × ${qty}` : s.name);
    return { id: s.id, name: s.name, qty, price, type: s.type, amount, label };
  }).filter(s => s.amount > 0);

  // Trip Fee — first-class line item, always shown unless waived. Defaults to $50.
  // For legacy estimates without tripFee set, this is a no-op (tripFee undefined → 0).
  const tripFeeAmount = pricing.tripFeeWaived ? 0 : (parseFloat(pricing.tripFee) || 0);
  if (tripFeeAmount > 0) {
    surchargeLines.push({
      id: '__trip_fee', name: 'Trip Fee', qty: 1, price: tripFeeAmount,
      type: 'flat', amount: tripFeeAmount, label: 'Trip Fee',
    });
  }
  const surchargesTotal = surchargeLines.reduce((sum, s) => sum + s.amount, 0);

  const subtotal = baseSubtotal + surchargesTotal;
  const discountAmt = pricing.discountType === 'pct'
    ? subtotal * (parseFloat(pricing.discount) || 0) / 100
    : (parseFloat(pricing.discount) || 0);
  const total = subtotal - discountAmt;

  // Not-to-Exceed (NTE) — price ceiling guarantee. Defaults to ON with hours+1.
  // Customer sees "Not to Exceed: $X" alongside the estimated total. nteHours: null
  // means auto-compute (hours+1); typing a number locks it manually.
  const nteEnabled = pricing.nteEnabled !== false;
  const nteHoursManual = pricing.nteHours;
  const effectiveNteHours = nteEnabled
    ? (nteHoursManual != null && Number(nteHoursManual) > 0
        ? Number(nteHoursManual)
        : (hours + 1))
    : null;
  // NTE total = labor at NTE hours + everything else flat (surcharges, fees) - discount
  const nteLaborTotal = nteEnabled ? effectiveNteHours * crewRate : 0;
  const nteTotal = nteEnabled
    ? Math.max(0, nteLaborTotal + truckTotal + fuelTotal + insuranceTotal + packingTotal + surchargesTotal - discountAmt)
    : null;

  return { laborTotal, truckTotal, fuelTotal, insuranceTotal, packingTotal, surchargeLines, surchargesTotal, baseSubtotal, subtotal, discountAmt, total,
           nteEnabled, nteHours: effectiveNteHours, nteTotal };
}

function DocHeader({ estimateNum, date, isInvoice }) {
  return (
    <div style={{ margin:'-40px -48px 36px', background:'#0c1a19', color:'#fff', padding:'44px 48px 36px', position:'relative', overflow:'hidden' }}>
      <div style={{position:'absolute',top:-160,right:-100,width:420,height:420,background:'radial-gradient(circle,rgba(20,198,191,.2) 0%,transparent 65%)',pointerEvents:'none'}}/>
      <div style={{position:'absolute',bottom:-200,left:-80,width:380,height:380,background:'radial-gradient(circle,rgba(61,204,126,.13) 0%,transparent 65%)',pointerEvents:'none'}}/>
      <div style={{position:'absolute',top:0,left:0,right:0,height:3,background:'linear-gradient(90deg,#14C6BF 0%,#3DCC7E 50%,transparent 100%)'}}/>

      <div style={{position:'relative', display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:40}}>
        <div style={{flex:'0 1 auto'}}>
          <img src="uploads/logo_tight.png" style={{ height: 44, width:'auto', objectFit:'contain', filter:'brightness(0) invert(1)', marginBottom:22, display:'block' }} alt="MaxAMove"/>
          <div style={{ fontSize:10, fontWeight:700, textTransform:'uppercase', letterSpacing:'.18em', color:'#3DCC7E', marginBottom:6 }}>From</div>
          <div style={{ fontSize:15, fontWeight:600, color:'#fff', marginBottom:8, letterSpacing:'-.01em' }}>MaxAMove LLC</div>
          <div style={{ fontSize:13, color:'rgba(255,255,255,.62)', lineHeight:1.75 }}>
            (615) 545-9735<br/>
            gavin@maxamove.com<br/>
            maxamove.com
          </div>
        </div>

        <div style={{ textAlign:'right', flex:'0 0 auto' }}>
          <div style={{ fontSize:11, fontWeight:700, textTransform:'uppercase', letterSpacing:'.22em', color:'rgba(255,255,255,.5)', marginBottom:10 }}>
            {isInvoice ? 'Invoice' : 'Estimate'}
          </div>
          <div style={{ fontSize:44, fontWeight:800, letterSpacing:'-.035em', lineHeight:1, marginBottom:18, color:'#fff', fontVariantNumeric:'tabular-nums' }}>
            #{estimateNum}
          </div>
          <div style={{ display:'inline-grid', gridTemplateColumns:'auto auto', gap:'4px 20px', fontSize:12.5, lineHeight:1.6, textAlign:'left' }}>
            <div style={{color:'rgba(255,255,255,.5)',textTransform:'uppercase',letterSpacing:'.1em',fontSize:10.5,fontWeight:700,alignSelf:'center'}}>Issued</div>
            <div style={{color:'#fff',fontWeight:500,fontVariantNumeric:'tabular-nums'}}>{date}</div>
            <div style={{color:'rgba(255,255,255,.5)',textTransform:'uppercase',letterSpacing:'.1em',fontSize:10.5,fontWeight:700,alignSelf:'center'}}>{isInvoice ? 'Due' : 'Valid'}</div>
            <div style={{color:'#fff',fontWeight:500}}>{isInvoice ? 'Upon Receipt' : '30 days'}</div>
          </div>
        </div>
      </div>
    </div>
  );
}

function InfoBlock({ label, value }) {
  return (
    <div style={{ marginBottom: 6 }}>
      <span style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--text-muted)' }}>{label}: </span>
      <span style={{ fontSize: 14, color: 'var(--text)', fontWeight: 500 }}>{value || '—'}</span>
    </div>
  );
}

// Lead pipeline stages — used by the Lead Status chip row on EstimatePreview.
// Order matches your pipeline visually: discovery → quoted → soft hold → confirmed → done.
const LEAD_STAGES = [
  { id: 'new',       label: 'New',       color: '#94a3b8' },
  { id: 'quoted',    label: 'Quoted',    color: '#0ea5e9' },
  { id: 'tentative', label: 'Tentative', color: '#a855f7' },
  { id: 'booked',    label: 'Booked',    color: '#16a34a' },
  { id: 'completed', label: 'Completed', color: '#475569' },
];

function EstimatePreview({ state, onStatusChange }) {
  const { customer, move, inventory, pricing, estimateNum, status } = state;
  const totals = calcTotals(pricing, move);
  const today = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
  const [shareModal, setShareModal] = useState(null); // {url, token}

  // ── Lead Status + Signature Override ──────────────────────────────────
  // Lets Gavin set the linked lead's pipeline stage directly from the estimate page,
  // and override the signature requirement when a customer agrees over phone / in person.
  // The override synthesizes a portal-accept queue item so the existing drainer in
  // appv2.jsx (line ~680) handles all downstream effects: estimate→signed, lead→booked,
  // job auto-created. Single source of truth — no parallel pathway to keep in sync.
  const [linkedLead, setLinkedLead] = useState(null); // { id, stage } or null
  const [stageBusy, setStageBusy] = useState(false);
  const [overrideOpen, setOverrideOpen] = useState(false);
  const [overrideTentative, setOverrideTentative] = useState(false);

  React.useEffect(() => {
    if (!estimateNum || !window.sb) return;
    let cancelled = false;
    (async () => {
      try {
        const { data: estRow } = await window.sb.from('estimates')
          .select('id, lead_id').eq('number', estimateNum).maybeSingle();
        if (cancelled) return;
        if (!estRow || !estRow.lead_id) { setLinkedLead(null); return; }
        const { data: leadRow } = await window.sb.from('leads')
          .select('id, stage').eq('id', estRow.lead_id).maybeSingle();
        if (cancelled) return;
        if (leadRow) setLinkedLead({ id: leadRow.id, stage: leadRow.stage || 'new' });
      } catch (e) { console.warn('[lead-status] fetch failed', e); }
    })();
    return () => { cancelled = true; };
  }, [estimateNum, status]);

  const setLeadStage = async (newStage) => {
    if (!linkedLead || stageBusy) return;
    if (linkedLead.stage === newStage) return;
    setStageBusy(true);
    setLinkedLead({ ...linkedLead, stage: newStage }); // optimistic
    try {
      if (typeof window.moveLeadStage === 'function') {
        await window.moveLeadStage(linkedLead.id, newStage);
      } else if (window.sb) {
        await window.sb.from('leads').update({ stage: newStage }).eq('id', linkedLead.id);
      }
    } catch (e) { console.warn('[lead-status] update failed', e); }
    finally { setStageBusy(false); }
  };

  const overrideSignature = () => {
    // Push a synthetic accept onto the queue. appv2's drainer picks it up within ~4s,
    // updates the estimate to status='signed', flips the lead, and creates the job.
    try {
      const q = JSON.parse(localStorage.getItem('crm_portal_accepts') || '[]');
      q.push({
        token: state._portalToken || ('override-' + (estimateNum || 'est').toLowerCase().replace(/[^a-z0-9]/g, '-') + '-' + Date.now().toString(36).slice(-4)),
        estimateNum,
        customer,
        move: state.move,
        total: totals.total,
        depositPaid: 0,
        depositMethod: null,
        acceptedAt: new Date().toISOString(),
        customerSig: 'OVERRIDE: marked booked without customer signature',
        reservationType: overrideTentative ? 'tentative' : 'guaranteed',
        // Signature was bypassed by the owner — portal hides the signature block on the booked screen.
        signatureRequired: false,
        processed: false,
      });
      localStorage.setItem('crm_portal_accepts', JSON.stringify(q));
    } catch (e) { console.warn('[override] queue write failed', e); }
    onStatusChange('accepted');
    // Optimistically reflect the new lead stage too — drainer will reconcile shortly.
    if (linkedLead) setLinkedLead({ ...linkedLead, stage: overrideTentative ? 'tentative' : 'booked' });
    setOverrideOpen(false);
    setOverrideTentative(false);
  };

  // Poll for customer acceptance from EITHER same-device localStorage OR cross-device Supabase.
  // Fires the queue on every NEW signed_at/acceptedAt timestamp so upgrades (tentative → guaranteed)
  // re-fire and the drainer in appv2.jsx updates the lead/job correctly.
  const lastAcceptedAtRef = React.useRef(null);
  React.useEffect(() => {
    if (!state._portalToken && !estimateNum) return;
    const fireQueueItem = (acceptedAt, customerSig, depositPaid, depositMethod, reservationType) => {
      if (!acceptedAt || acceptedAt === lastAcceptedAtRef.current) return;
      lastAcceptedAtRef.current = acceptedAt;
      if (status !== 'accepted') onStatusChange('accepted');
      try {
        const q = JSON.parse(localStorage.getItem('crm_portal_accepts') || '[]');
        q.push({
          token: state._portalToken,
          estimateNum: state.estimateNum,
          customer: state.customer,
          move: state.move,
          total: totals.total,
          depositPaid: depositPaid || 0,
          depositMethod: depositMethod || null,
          acceptedAt,
          customerSig,
          reservationType: reservationType || 'guaranteed',
          processed: false,
        });
        localStorage.setItem('crm_portal_accepts', JSON.stringify(q));
      } catch {}
    };

    const check = async () => {
      // (1) Same-device localStorage check (fast, no network)
      if (state._portalToken) {
        try {
          const shares = JSON.parse(localStorage.getItem('crm_portal_shares') || '{}');
          const s = shares[state._portalToken];
          if (s && s.data && s.data.acceptedAt) {
            fireQueueItem(s.data.acceptedAt, s.data.customerSig, s.data.depositPaid, s.data.depositMethod, s.data.reservationType);
          }
        } catch {}
      }
      // (2) Cross-device Supabase check — catches accepts from the customer's phone
      //     while Gavin's CRM is open on his laptop. Reads by estimate number (unique).
      if (estimateNum && window.sb) {
        try {
          const { data: row } = await window.sb
            .from('estimates')
            .select('signed_at, signature_data, pricing, status')
            .eq('number', estimateNum)
            .maybeSingle();
          if (row && row.signed_at) {
            const p = row.pricing || {};
            fireQueueItem(row.signed_at, row.signature_data, p.depositPaid, p.depositMethod, p.reservationType);
          }
        } catch {}
      }
    };
    check();
    const iv = setInterval(check, 4000);
    const onFocus = () => check();
    window.addEventListener('focus', onFocus);
    return () => { clearInterval(iv); window.removeEventListener('focus', onFocus); };
  }, [state._portalToken, estimateNum, status]);

  const sendToCustomer = () => {
    const token = 'est-' + estimateNum.toLowerCase().replace(/[^a-z0-9]/g, '-') + '-' + Date.now().toString(36).slice(-4);
    // Compute the REAL deposit amount based on per-estimate toggle/mode/value (not hardcoded 20%)
    const depReq = pricing.depositRequired !== false;
    const depMode = pricing.depositMode || 'pct';
    const depVal = parseFloat(pricing.depositValue) || 0;
    const depositAmount = depReq
      ? (depMode === 'pct' ? +(totals.total * depVal / 100).toFixed(2) : +depVal.toFixed(2))
      : 0;
    const payload = {
      kind: 'estimate',
      data: {
        id: estimateNum,
        customer: `${customer.firstName} ${customer.lastName}`.trim() || 'Customer',
        firstName: customer.firstName, lastName: customer.lastName,
        email: customer.email, phone: customer.phone,
        moveDate: move.moveDate, moveSize: move.moveSize,
        arrivalType: move.arrivalType, arrivalTime: move.arrivalTime, arrivalWindowEnd: move.arrivalWindowEnd,
        fromAddress: move.fromAddress, toAddress: move.toAddress,
        distance: move.distance, specialInstructions: move.specialInstructions,
        inventory, pricing, totals,
        estimatedTotal: totals.total,
        // Honest deposit fields (no more hardcoded 20%):
        depositRequired: depReq,
        depositMode: depMode,
        depositValue: depVal,
        depositAmount,
        // Tentative reservation toggle — when true, portal shows TWO accept buttons.
        allowTentative: !!pricing.allowTentative,
        // Not-to-Exceed price guarantee — shown to customer if enabled.
        nteEnabled: !!totals.nteEnabled,
        nteHours: totals.nteHours,
        nteTotal: totals.nteTotal,
        status: 'sent',
      },
      created: Date.now(),
    };
    const shares = (() => { try { return JSON.parse(localStorage.getItem('crm_portal_shares') || '{}'); } catch { return {}; } })();
    shares[token] = payload;
    localStorage.setItem('crm_portal_shares', JSON.stringify(shares));
    // Persist token on estimate state so we can poll for customer acceptance
    state._portalToken = token;
    try { const s = JSON.parse(localStorage.getItem('mam_state')); s._portalToken = token; localStorage.setItem('mam_state', JSON.stringify(s)); } catch {}
    onStatusChange('sent');
    // Build a CROSS-DEVICE-COMPATIBLE link by ALSO inline-encoding the payload as
    // base64url. Customer's browser can read it without needing localStorage.
    let inline = '';
    try {
      const json = JSON.stringify(payload);
      const b64 = btoa(unescape(encodeURIComponent(json)))
        .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
      inline = '&d=' + b64;
    } catch (e) {
      // Inline encode failed (huge payload?) — fall back to token-only link.
      console.warn('[portal] inline encode failed, using token-only link', e);
    }
    const url = new URL('portal.html#t=' + token + inline, window.location.href).href;
    setShareModal({ url, token });
  };

  const lineItems = [
    pricing.movers > 0 && pricing.hours > 0 && { label: `Labor — ${pricing.movers} movers × ${pricing.hours} hrs @ $${pricing.hourlyRate}/hr`, amount: totals.laborTotal },
    parseFloat(pricing.truckFee) > 0 && { label: `Truck Rental — ${pricing.truckSize} truck`, amount: totals.truckTotal },
    totals.fuelTotal > 0 && { label: `Fuel & Mileage — ${move.distance} mi × $${pricing.fuelRate}/mi`, amount: totals.fuelTotal },
    totals.insuranceTotal > 0 && { label: `Valuation Coverage`, amount: totals.insuranceTotal },
    parseFloat(pricing.packingFee) > 0 && { label: `Packing Materials`, amount: totals.packingTotal },
    ...(totals.surchargeLines || []).map(s => ({ label: s.label, amount: s.amount })),
  ].filter(Boolean);

  return (
    <div>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
        <div>
          <h2 style={{ margin: 0, fontSize: 22, fontWeight: 800 }}>Estimate Preview</h2>
          <div style={{ fontSize: 13, color: 'var(--text-muted)', marginTop: 2 }}>This is how the customer will see it</div>
        </div>
        <div style={{ display: 'flex', gap: 10 }}>
          <StatusBadge status={status} />
          <button onClick={() => window.print()} style={{...btnStyle('secondary'),display:'inline-flex',alignItems:'center',gap:5}}><Icon name="file-text" size={13}/> Print / PDF</button>
          {status !== 'accepted' && (
            <button onClick={sendToCustomer} style={{...btnStyle('primary'),display:'inline-flex',alignItems:'center',gap:5}}>
              {status === 'sent' ? <><Icon name="arrow-right" size={13} color="#fff"/> Resend Portal Link</> : <><Icon name="send" size={13} color="#fff"/> Send to Customer</>}
            </button>
          )}
          {status === 'sent' && (
            <button onClick={() => onStatusChange('accepted')} style={{...btnStyle('success'),display:'inline-flex',alignItems:'center',gap:5}} title="Manually mark accepted (e.g. signed in person)"><Icon name="check-circle" size={13} color="#fff"/> Mark Accepted</button>
          )}
        </div>
      </div>

      {/* === LEAD STATUS + OVERRIDE SIGNATURE controls === */}
      <div style={{ marginBottom: 16, padding: 14, background: 'var(--bg)', borderRadius: 10, border: '1px solid var(--border)' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 14, flexWrap: 'wrap' }}>
          <div>
            <div style={{ fontSize: 11, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '.08em', color: 'var(--text-muted)', marginBottom: 4 }}>Lead status</div>
            <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>
              {linkedLead ? 'Click a stage to update where this lead sits in your pipeline.' : 'Save the estimate first to link a lead.'}
            </div>
          </div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {LEAD_STAGES.map(s => {
              const active = linkedLead && linkedLead.stage === s.id;
              const disabled = !linkedLead || stageBusy;
              return (
                <button key={s.id} type="button" onClick={() => setLeadStage(s.id)} disabled={disabled}
                  style={{
                    padding: '6px 12px', borderRadius: 8,
                    border: active ? `2px solid ${s.color}` : '1px solid var(--border)',
                    background: active ? s.color : 'var(--card-bg, #fff)',
                    color: active ? '#fff' : 'var(--text)',
                    fontWeight: 700, fontSize: 12,
                    cursor: disabled ? 'not-allowed' : 'pointer',
                    opacity: linkedLead ? 1 : 0.5,
                    fontFamily: 'inherit',
                    transition: 'background .15s, color .15s, border-color .15s',
                  }}>
                  {s.label}
                </button>
              );
            })}
          </div>
        </div>
        {status !== 'accepted' && (
          <div style={{ marginTop: 12, paddingTop: 10, borderTop: '1px dashed var(--border)', display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
            <div style={{ fontSize: 12, color: 'var(--text-muted)', display: 'inline-flex', alignItems: 'center', gap: 5 }}>
              <Icon name="phone" size={12}/> Booked over the phone or in person? Skip the customer signature.
            </div>
            <button type="button" onClick={() => setOverrideOpen(true)}
              style={{ padding: '8px 14px', borderRadius: 8, border: '1px dashed var(--accent)', background: 'transparent', color: 'var(--accent)', fontFamily: 'inherit', fontSize: 12, fontWeight: 700, cursor: 'pointer' }}>
              Override signature →
            </button>
          </div>
        )}
      </div>

      {/* Override confirm modal */}
      {overrideOpen && (
        <div onClick={() => setOverrideOpen(false)} style={{ position: 'fixed', inset: 0, background: 'rgba(15,23,42,.55)', zIndex: 9998, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
          <div onClick={e => e.stopPropagation()} style={{ width: '100%', maxWidth: 460, background: 'var(--card, #fff)', borderRadius: 14, boxShadow: '0 24px 60px rgba(15,23,42,.35)', overflow: 'hidden' }}>
            <div style={{ padding: '18px 20px 6px' }}>
              <h3 style={{ margin: 0, fontSize: 18, fontWeight: 800 }}>Override signature</h3>
              <p style={{ marginTop: 8, marginBottom: 0, fontSize: 13, color: 'var(--text-muted)', lineHeight: 1.5 }}>
                Marks the estimate as signed without a customer signature, moves the lead to <b>{overrideTentative ? 'Tentative' : 'Booked'}</b>, and {overrideTentative ? 'holds the date — no job will be created yet.' : 'auto-creates the job.'} Audit trail will record "OVERRIDE" in the signature field.
              </p>
            </div>
            <label style={{ display: 'flex', gap: 10, padding: '14px 20px', cursor: 'pointer', alignItems: 'flex-start', borderTop: '1px solid var(--border)', borderBottom: '1px solid var(--border)', background: overrideTentative ? '#faf5ff' : 'transparent' }}>
              <input type="checkbox" checked={overrideTentative} onChange={e => setOverrideTentative(e.target.checked)}
                style={{ width: 18, height: 18, marginTop: 2, accentColor: '#a855f7', cursor: 'pointer' }} />
              <div>
                <div style={{ fontWeight: 700, fontSize: 13 }}>Hold tentatively (no job yet)</div>
                <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 2 }}>Leave unchecked to confirm a full booking with auto-created job.</div>
              </div>
            </label>
            <div style={{ padding: 16, display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
              <button onClick={() => setOverrideOpen(false)} style={btnStyle('secondary')}>Cancel</button>
              <button onClick={overrideSignature}
                style={{ ...btnStyle('success'), background: overrideTentative ? '#a855f7' : '#16a34a' }}>
                <span style={{display:'inline-flex',alignItems:'center',gap:5}}>{overrideTentative ? <><Icon name="clock" size={13} color="#fff"/> Hold tentatively</> : <><Icon name="check" size={13} color="#fff"/> Mark booked</>}</span>
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Portal status banner */}
      {state._portalToken && status === 'sent' && (
        <div style={{ marginBottom: 16, padding: '12px 16px', background: '#eff6ff', border: '1px solid #bfdbfe', borderRadius: 10, display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
          <div style={{ fontSize: 13, color: '#1e40af', display: 'flex', alignItems: 'flex-start', gap: 7 }}>
            <Icon name="mail" size={14} style={{flexShrink:0,marginTop:2}}/>
            <div><strong>Portal link active.</strong> Waiting on {customer.firstName || 'customer'} to review & accept. We'll auto-detect when they sign.</div>
          </div>
          <button onClick={() => {
            const url = new URL('portal.html#t=' + state._portalToken, window.location.href).href;
            setShareModal({ url, token: state._portalToken });
          }} style={{ ...btnStyle('secondary'), fontSize: 12, padding: '6px 12px' }}>View Link</button>
        </div>
      )}
      {status === 'accepted' && (
        <div style={{ marginBottom: 16, padding: '12px 16px', background: '#f0fdf4', border: '1px solid #bbf7d0', borderRadius: 10, fontSize: 13, color: '#166534', display: 'flex', alignItems: 'flex-start', gap: 7 }}>
          <Icon name="check-circle" size={14} style={{flexShrink:0,marginTop:2}}/>
          <div><strong>Accepted by customer.</strong> Lead moved to Booked · job auto-created on the Jobs Board.</div>
        </div>
      )}

      {shareModal && <SharePortalModal share={shareModal} customer={customer} pricing={pricing} totals={totals} onClose={() => setShareModal(null)} />}

      <div className="print-doc" style={{ background: '#fff', borderRadius: 6, border: '1px solid var(--border)', padding: '40px 48px', boxShadow: '0 10px 40px rgba(12,26,25,0.08)', color: '#1a1a1a', overflow:'hidden' }}>
        <DocHeader estimateNum={estimateNum} date={today} isInvoice={false} />

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 32, marginBottom: 32 }}>
          <div>
            <div style={{ fontSize: 11, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.1em', color: '#999', marginBottom: 10 }}>Bill To</div>
            <div style={{ fontWeight: 700, fontSize: 16, marginBottom: 4 }}>{customer.firstName} {customer.lastName}</div>
            <div style={{ fontSize: 14, color: '#555', lineHeight: 1.7 }}>
              {customer.email && <div>{customer.email}</div>}
              {customer.phone && <div>{customer.phone}</div>}
              {customer.currentAddress && <div>{customer.currentAddress}</div>}
            </div>
          </div>
          <div>
            <div style={{ fontSize: 11, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.1em', color: '#999', marginBottom: 10 }}>Move Details</div>
            <InfoBlock label="Date" value={move.moveDate ? new Date(move.moveDate + 'T12:00').toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) : null} />
            <InfoBlock label="Size" value={move.moveSize} />
            <InfoBlock label="From" value={move.fromAddress} />
            <InfoBlock label="To" value={move.toAddress} />
            {move.distance && <InfoBlock label="Distance" value={`${move.distance} miles`} />}
          </div>
        </div>

        {inventory.length > 0 && (
          <div style={{ marginBottom: 28 }}>
            <div style={{ fontSize: 11, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.1em', color: '#999', marginBottom: 12 }}>Inventory Summary</div>
            {(() => {
              // Group by room
              const byRoom = {};
              inventory.forEach(item => {
                const r = item.room || '_unassigned';
                if (!byRoom[r]) byRoom[r] = [];
                byRoom[r].push(item);
              });
              const roomLabels = {
                living:    { icon: 'sofa',      text: 'Living Room' },
                kitchen:   { icon: 'chef-hat',  text: 'Kitchen' },
                dining:    { icon: 'utensils',  text: 'Dining Room' },
                garage:    { icon: 'car',       text: 'Garage' },
                patio:     { icon: 'tree',      text: 'Patio / Deck' },
                shed:      { icon: 'home',      text: 'Shed' },
                basement:  { icon: 'arrow-down',text: 'Basement' },
                attic:     { icon: 'arrow-up',  text: 'Attic' },
                office:    { icon: 'briefcase', text: 'Office' },
                laundry:   { icon: 'box',       text: 'Laundry' },
                storage:   { icon: 'box',       text: 'Storage' },
                other:     { icon: 'sparkles',  text: 'Other' },
                _unassigned:{ icon: 'box',      text: 'Items' },
              };
              const labelFor = (rid) => {
                if (roomLabels[rid]) return roomLabels[rid];
                const m = String(rid).match(/^bedroom(\d+)$/);
                if (m) return { icon: 'bed', text: `Bedroom ${m[1]}` };
                return { icon: 'box', text: rid };
              };
              return Object.keys(byRoom).map(rid => {
                const roomItems = byRoom[rid];
                const roomWeight = roomItems.reduce((s, it) => s + (window.getItemWeight ? window.getItemWeight(it) : 0) * (it.qty || 1), 0);
                return (
                  <div key={rid} style={{ marginBottom: 14 }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 12px', background: '#f0fdfb', borderRadius: 6, marginBottom: 4, fontSize: 12, fontWeight: 700, color: '#0a9e98' }}>
                      <span style={{display:'inline-flex',alignItems:'center',gap:6}}><Icon name={labelFor(rid).icon} size={13}/> {labelFor(rid).text}</span>
                      <span>{roomItems.reduce((s,it)=>s+(parseInt(it.qty)||0),0)} item(s) · {roomWeight.toLocaleString()} lbs</span>
                    </div>
                    <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 14 }}>
                      <tbody>
                        {roomItems.map((item, i) => {
                          const w = (window.getItemWeight ? window.getItemWeight(item) : 0) * (item.qty || 1);
                          const cat = (window.INVENTORY_CATEGORIES || []).find(c => c.id === item.categoryId);
                          return (
                            <tr key={item.id} style={{ borderBottom: '1px solid #f0f0f0' }}>
                              <td style={{ padding: '6px 12px', fontWeight: 500, width: '55%' }}>{cat ? <span style={{marginRight:6}}>{cat.icon}</span> : null}{item.name || item.item || '—'}{item.size ? <span style={{ marginLeft: 6, fontSize: 10, color: '#999', textTransform: 'uppercase' }}>· {item.size}</span> : null}</td>
                              <td style={{ padding: '6px 12px', width: '10%' }}>×{item.qty}</td>
                              <td style={{ padding: '6px 12px', width: '15%', color:'#666' }}>{w ? `${w.toLocaleString()} lbs` : '—'}</td>
                              <td style={{ padding: '6px 12px', color: '#666', fontSize: 12 }}>{item.notes || ''}</td>
                            </tr>
                          );
                        })}
                      </tbody>
                    </table>
                  </div>
                );
              });
            })()}
          </div>
        )}

        <div style={{ marginBottom: 28 }}>
          <div style={{ fontSize: 11, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.1em', color: '#999', marginBottom: 12 }}>Services & Fees</div>
          <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 14 }}>
            <thead>
              <tr style={{ borderBottom: '2px solid #eee' }}>
                <th style={{ textAlign: 'left', padding: '6px 8px', fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em', color: '#999' }}>Description</th>
                <th style={{ textAlign: 'right', padding: '6px 8px', fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em', color: '#999' }}>Amount</th>
              </tr>
            </thead>
            <tbody>
              {lineItems.map((item, i) => (
                <tr key={i} style={{ borderBottom: '1px solid #f0f0f0' }}>
                  <td style={{ padding: '10px 8px' }}>{item.label}</td>
                  <td style={{ padding: '10px 8px', textAlign: 'right', fontWeight: 600 }}>${item.amount.toFixed(2)}</td>
                </tr>
              ))}
              {totals.discountAmt > 0 && (
                <tr style={{ borderBottom: '1px solid #f0f0f0' }}>
                  <td style={{ padding: '10px 8px', color: '#16a34a' }}>Discount</td>
                  <td style={{ padding: '10px 8px', textAlign: 'right', fontWeight: 600, color: '#16a34a' }}>-${totals.discountAmt.toFixed(2)}</td>
                </tr>
              )}
            </tbody>
            <tfoot>
              <tr style={{ borderTop: '2px solid #eee' }}>
                <td style={{ padding: '14px 8px', fontWeight: 800, fontSize: 16 }}>Estimated Total</td>
                <td style={{ padding: '14px 8px', textAlign: 'right', fontWeight: 900, fontSize: 20, color: 'var(--accent)' }}>${totals.total.toFixed(2)}</td>
              </tr>
              {totals.nteEnabled && totals.nteTotal != null && (
                <tr>
                  <td style={{ padding: '8px 8px 14px', fontWeight: 700, fontSize: 13, color: '#0a9e98' }}><span style={{display:'inline-flex',alignItems:'center',gap:5}}><Icon name="lock" size={12}/> Not to Exceed ({totals.nteHours} hrs)</span></td>
                  <td style={{ padding: '8px 8px 14px', textAlign: 'right', fontWeight: 800, fontSize: 15, color: '#0a9e98' }}>${totals.nteTotal.toFixed(2)}</td>
                </tr>
              )}
            </tfoot>
          </table>
          {totals.nteEnabled && totals.nteTotal != null && (
            <div style={{ marginTop: 12, padding: '10px 14px', background: '#f0fdfb', borderRadius: 10, border: '1px solid var(--accent)', fontSize: 12, color: 'var(--accent-dark)', fontWeight: 600 }}>
              <span style={{display:'inline-flex',alignItems:'flex-start',gap:6}}><Icon name="check" size={13} style={{flexShrink:0,marginTop:2}}/><span><b>Price guarantee:</b> Even if your move runs longer than estimated, you'll never pay more than <b>${totals.nteTotal.toFixed(2)}</b>.</span></span>
            </div>
          )}
        </div>

        {move.specialInstructions && (
          <div style={{ marginBottom: 24, padding: 16, background: '#fffbeb', borderRadius: 10, border: '1px solid #fde68a' }}>
            <div style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: '#92400e', marginBottom: 6 }}>Special Instructions</div>
            <div style={{ fontSize: 14, color: '#78350f' }}>{move.specialInstructions}</div>
          </div>
        )}

        <div style={{ marginTop: 28, padding: '20px 24px', background: '#f9f9f9', borderRadius: 12, fontSize: 12, color: '#666', lineHeight: 1.7 }}>
          <div style={{ fontWeight: 700, marginBottom: 6, fontSize: 13, color: '#333' }}>Terms & Conditions</div>
          {(() => {
            const depositRequired = pricing.depositRequired !== false;
            const mode = pricing.depositMode || 'pct';
            const val = parseFloat(pricing.depositValue) || 0;
            const depositAmt = mode === 'pct' ? totals.total * val / 100 : val;
            let depositSentence;
            if (!depositRequired || depositAmt <= 0) {
              depositSentence = 'No deposit is required to confirm this booking — your move date is held when you accept this estimate.';
            } else if (mode === 'pct') {
              depositSentence = `A deposit of ${val}% ($${depositAmt.toFixed(2)}) is required to confirm your booking.`;
            } else {
              depositSentence = `A deposit of $${val.toFixed(2)} is required to confirm your booking.`;
            }
            return `This estimate is valid for 30 days from the date issued. Final charges are based on actual time, weight, and conditions at the time of the move. ${depositSentence} Cancellations within 48 hours of the move date may result in a cancellation fee. MaxAMove is not responsible for items not declared on the inventory list. By accepting this estimate, you agree to these terms.`;
          })()}
        </div>

        <div style={{ marginTop: 32, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 32 }}>
          <div>
            <div style={{ fontSize: 12, color: '#999', marginBottom: 6 }}>Customer Signature</div>
            <div style={{ borderBottom: '1.5px solid #ddd', height: 40, marginBottom: 6 }}></div>
            <div style={{ fontSize: 11, color: '#bbb' }}>Signature / Date</div>
          </div>
          <div>
            <div style={{ fontSize: 12, color: '#999', marginBottom: 6 }}>Authorized by MaxAMove LLC</div>
            <div style={{ borderBottom: '1.5px solid #ddd', height: 40, marginBottom: 6 }}></div>
            <div style={{ fontSize: 11, color: '#bbb' }}>Signature / Date</div>
          </div>
        </div>
      </div>
    </div>
  );
}

function InvoiceView({ state, onStatusChange }) {
  const { customer, move, inventory, pricing, estimateNum, status } = state;
  const totals = calcTotals(pricing, move);
  const today = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
  const invoiceNum = estimateNum.replace('MM', 'INV');

  const lineItems = [
    pricing.movers > 0 && pricing.hours > 0 && { label: `Labor — ${pricing.movers} movers × ${pricing.hours} hrs @ $${pricing.hourlyRate}/hr`, amount: totals.laborTotal },
    parseFloat(pricing.truckFee) > 0 && { label: `Truck Rental — ${pricing.truckSize} truck`, amount: totals.truckTotal },
    totals.fuelTotal > 0 && { label: `Fuel & Mileage — ${move.distance} mi × $${pricing.fuelRate}/mi`, amount: totals.fuelTotal },
    totals.insuranceTotal > 0 && { label: `Valuation Coverage`, amount: totals.insuranceTotal },
    parseFloat(pricing.packingFee) > 0 && { label: `Packing Materials`, amount: totals.packingTotal },
    ...(totals.surchargeLines || []).map(s => ({ label: s.label, amount: s.amount })),
  ].filter(Boolean);

  return (
    <div>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
        <div>
          <h2 style={{ margin: 0, fontSize: 22, fontWeight: 800 }}>Invoice</h2>
          <div style={{ fontSize: 13, color: 'var(--text-muted)', marginTop: 2 }}>Ready to send to customer</div>
        </div>
        <div style={{ display: 'flex', gap: 10 }}>
          <StatusBadge status={status} />
          <button onClick={() => window.print()} style={{...btnStyle('secondary'),display:'inline-flex',alignItems:'center',gap:5}}><Icon name="file-text" size={13}/> Print / PDF</button>
          {status !== 'paid' && (
            <button onClick={() => onStatusChange('paid')} style={{...btnStyle('success'),display:'inline-flex',alignItems:'center',gap:5}}><Icon name="dollar-bill" size={13} color="#fff"/> Mark as Paid</button>
          )}
        </div>
      </div>

      <div className="print-doc" style={{ background: '#fff', borderRadius: 6, border: '1px solid var(--border)', padding: '40px 48px', boxShadow: '0 10px 40px rgba(12,26,25,0.08)', color: '#1a1a1a', overflow:'hidden' }}>
        <DocHeader estimateNum={invoiceNum} date={today} isInvoice={true} />

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 32, marginBottom: 32 }}>
          <div>
            <div style={{ fontSize: 11, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.1em', color: '#999', marginBottom: 10 }}>Bill To</div>
            <div style={{ fontWeight: 700, fontSize: 16, marginBottom: 4 }}>{customer.firstName} {customer.lastName}</div>
            <div style={{ fontSize: 14, color: '#555', lineHeight: 1.7 }}>
              {customer.email && <div>{customer.email}</div>}
              {customer.phone && <div>{customer.phone}</div>}
            </div>
          </div>
          <div>
            <div style={{ fontSize: 11, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.1em', color: '#999', marginBottom: 10 }}>Move Summary</div>
            <InfoBlock label="Date" value={move.moveDate ? new Date(move.moveDate + 'T12:00').toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }) : null} />
            <InfoBlock label="From" value={move.fromAddress} />
            <InfoBlock label="To" value={move.toAddress} />
            <InfoBlock label="Ref Estimate" value={estimateNum} />
          </div>
        </div>

        <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 14, marginBottom: 28 }}>
          <thead>
            <tr style={{ borderBottom: '2px solid #eee', background: '#fafafa' }}>
              <th style={{ textAlign: 'left', padding: '10px 12px', fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em', color: '#999' }}>Description</th>
              <th style={{ textAlign: 'right', padding: '10px 12px', fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em', color: '#999' }}>Amount</th>
            </tr>
          </thead>
          <tbody>
            {lineItems.map((item, i) => (
              <tr key={i} style={{ borderBottom: '1px solid #f0f0f0' }}>
                <td style={{ padding: '12px 12px' }}>{item.label}</td>
                <td style={{ padding: '12px 12px', textAlign: 'right', fontWeight: 600 }}>${item.amount.toFixed(2)}</td>
              </tr>
            ))}
            {totals.discountAmt > 0 && (
              <tr style={{ borderBottom: '1px solid #f0f0f0' }}>
                <td style={{ padding: '12px 12px', color: '#16a34a' }}>Discount Applied</td>
                <td style={{ padding: '12px 12px', textAlign: 'right', fontWeight: 600, color: '#16a34a' }}>-${totals.discountAmt.toFixed(2)}</td>
              </tr>
            )}
          </tbody>
          <tfoot>
            <tr style={{ borderTop: '2px solid #222', background: '#fafafa' }}>
              <td style={{ padding: '16px 12px', fontWeight: 800, fontSize: 18 }}>Total Due</td>
              <td style={{ padding: '16px 12px', textAlign: 'right', fontWeight: 900, fontSize: 24, color: 'var(--accent)' }}>${totals.total.toFixed(2)}</td>
            </tr>
          </tfoot>
        </table>

        <div style={{ padding: '20px 24px', background: '#f0fdf4', borderRadius: 12, border: '1px solid #bbf7d0', marginBottom: 24, fontSize: 14, color: '#166534' }}>
          <b>Payment Methods:</b> Cash, Check (payable to MaxAMove LLC), Zelle (615-545-9735), Venmo — payment due upon completion of move.
        </div>

        <div style={{ marginTop: 24, padding: '20px 24px', background: '#f9f9f9', borderRadius: 12, fontSize: 12, color: '#666', lineHeight: 1.7 }}>
          Thank you for choosing MaxAMove! We appreciate your business. If you have any questions about this invoice, please contact us at gavin@maxamove.com or (615) 545-9735.
        </div>

        {status === 'paid' && (
          <div style={{ marginTop: 20, textAlign: 'center', padding: 20 }}>
            <div style={{ display: 'inline-block', border: '4px solid #16a34a', borderRadius: 12, padding: '10px 32px', color: '#16a34a', fontWeight: 900, fontSize: 28, letterSpacing: '0.12em', transform: 'rotate(-3deg)', opacity: 0.8 }}>PAID</div>
          </div>
        )}
      </div>
    </div>
  );
}

function SharePortalModal({ share, customer, pricing, totals, onClose }) {
  const [copied, setCopied] = React.useState(false);
  const firstName = customer.firstName || 'there';
  // Compute deposit phrasing for messages
  const depReq = pricing && pricing.depositRequired !== false;
  const depMode = (pricing && pricing.depositMode) || 'pct';
  const depVal = parseFloat(pricing && pricing.depositValue) || 0;
  const depAmt = depReq && totals
    ? (depMode === 'pct' ? (totals.total * depVal / 100) : depVal)
    : 0;
  const depositPhrase = !depReq || depAmt <= 0
    ? 'No deposit is required for this booking.'
    : (depMode === 'pct'
        ? `A ${depVal}% deposit ($${depAmt.toFixed(2)}) is required to confirm — you can pay it right from the link.`
        : `A $${depVal.toFixed(2)} deposit is required to confirm — you can pay it right from the link.`);
  const portalBlurb = !depReq || depAmt <= 0
    ? `They'll see the estimate and can accept it. No deposit collected for this one. When they sign, the lead auto-moves to Booked and a job is created.`
    : `They'll see the estimate, can accept it, and pay the ${depMode === 'pct' ? depVal + '%' : '$' + depVal.toFixed(2)} deposit right from the link. When they sign, the lead auto-moves to Booked and a job is created.`;
  const smsBody = encodeURIComponent(`Hey ${firstName}! Your estimate from MaxAMove is ready — tap to review (takes about 30 seconds): ${share.url}\n\nAny questions, just reply!`);
  const emailSubj = encodeURIComponent('Your MaxAMove Estimate');
  const emailBody = encodeURIComponent(`Hey ${firstName},\n\nThanks for reaching out — your estimate is ready! Tap below to review (takes about 30 seconds):\n\n${share.url}\n\n${depositPhrase}\n\nAny questions at all, just hit reply.\n\n— Gavin · MaxAMove`);
  const copy = () => { navigator.clipboard.writeText(share.url).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1800); }); };
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.55)', zIndex: 9999, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }} onClick={onClose}>
      <div onClick={e => e.stopPropagation()} style={{ background: '#fff', borderRadius: 16, padding: 28, width: '100%', maxWidth: 520, boxShadow: '0 20px 60px rgba(0,0,0,0.3)' }}>
        <div style={{ fontSize: 13, color: '#14C6BF', fontWeight: 800, textTransform: 'uppercase', letterSpacing: '.08em', marginBottom: 6 }}>Customer Portal Link</div>
        <div style={{ fontSize: 20, fontWeight: 900, fontFamily: 'Poppins', marginBottom: 4 }}>Send this to {customer.firstName || 'your customer'}</div>
        <div style={{ fontSize: 13, color: '#64748b', marginBottom: 18, lineHeight: 1.5 }}>{portalBlurb}</div>

        <div style={{ padding: '12px 14px', background: '#f5f7f9', border: '1.5px solid #e2e8f0', borderRadius: 10, fontSize: 12, color: '#334155', wordBreak: 'break-all', marginBottom: 12, fontFamily: 'ui-monospace, Menlo, monospace' }}>
          {share.url}
        </div>

        <button onClick={copy} style={{ width: '100%', padding: '12px', borderRadius: 10, background: copied ? '#16a34a' : 'linear-gradient(135deg,#14C6BF,#3DCC7E)', color: '#fff', border: 'none', fontWeight: 800, fontSize: 14, cursor: 'pointer', marginBottom: 10, fontFamily: 'inherit', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
          {copied ? <><Icon name="check" size={14} color="#fff"/> Copied to clipboard</> : <><Icon name="paperclip" size={14} color="#fff"/> Copy Link</>}
        </button>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8, marginBottom: 16 }}>
          {customer.phone && (
            <a href={`sms:${customer.phone.replace(/\D/g,'')}?&body=${smsBody}`} style={{ textAlign: 'center', padding: '10px', borderRadius: 9, border: '1.5px solid #e2e8f0', textDecoration: 'none', color: '#0f172a', fontWeight: 700, fontSize: 13, background: '#fff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 5 }}><Icon name="message" size={13}/> Text</a>
          )}
          {customer.email && (
            <a href={`mailto:${customer.email}?subject=${emailSubj}&body=${emailBody}`} style={{ textAlign: 'center', padding: '10px', borderRadius: 9, border: '1.5px solid #e2e8f0', textDecoration: 'none', color: '#0f172a', fontWeight: 700, fontSize: 13, background: '#fff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 5 }}><Icon name="mail" size={13}/> Email</a>
          )}
          <a href={share.url} target="_blank" rel="noopener" style={{ textAlign: 'center', padding: '10px', borderRadius: 9, border: '1.5px solid #e2e8f0', textDecoration: 'none', color: '#0f172a', fontWeight: 700, fontSize: 13, background: '#fff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 5 }}><Icon name="eye" size={13}/> Preview</a>
        </div>

        <div style={{ padding: '10px 12px', background: '#fffbeb', border: '1px solid #fde68a', borderRadius: 8, fontSize: 12, color: '#78350f', lineHeight: 1.5, marginBottom: 14, display: 'flex', alignItems: 'flex-start', gap: 7 }}>
          <Icon name="info" size={13} style={{flexShrink:0,marginTop:2}}/>
          <div>The portal link also works on the customer's phone — no account needed. When they accept, you'll see the status flip to <b>Accepted</b> here automatically.</div>
        </div>

        <button onClick={onClose} style={{ width: '100%', padding: '10px', borderRadius: 9, border: '1.5px solid #e2e8f0', background: 'transparent', fontWeight: 700, cursor: 'pointer', color: '#64748b', fontFamily: 'inherit' }}>Close</button>
      </div>
    </div>
  );
}

function StatusBadge({ status }) {
  const map = {
    draft: { label: 'Draft', bg: '#f3f4f6', color: '#6b7280' },
    sent: { label: 'Sent', bg: '#eff6ff', color: '#2563eb' },
    accepted: { label: 'Accepted', bg: '#f0fdf4', color: '#16a34a' },
    paid: { label: 'Paid', bg: '#f0fdf4', color: '#15803d' },
  };
  const s = map[status] || map.draft;
  return (
    <span style={{ padding: '6px 14px', borderRadius: 20, background: s.bg, color: s.color, fontWeight: 700, fontSize: 13 }}>{s.label}</span>
  );
}

function btnStyle(type) {
  const styles = {
    primary: { background: 'var(--accent)', color: '#fff', border: 'none' },
    secondary: { background: 'var(--card-bg)', color: 'var(--text)', border: '1.5px solid var(--border)' },
    success: { background: '#16a34a', color: '#fff', border: 'none' },
  };
  return {
    padding: '9px 18px', borderRadius: 10, cursor: 'pointer',
    fontSize: 13, fontWeight: 700, fontFamily: 'inherit',
    ...styles[type]
  };
}

Object.assign(window, { EstimatePreview, InvoiceView, StatusBadge, SharePortalModal, btnStyle, calcTotals });
