// Subcontractor Application page
//
// Submits to FabTrk as a real application (Talent → Subcontractors), through the
// same org embed token the quote form uses.
//
// NOTE ON NAMING: every .jsx here is a sibling <script>, so they share one global
// lexical scope and Babel emits real `const`, so a name another file already
// declares is a SyntaxError that blanks the entire site. Helpers below are
// prefixed `sub…` for that reason, and quote.jsx's are read off `window` rather
// than redeclared.

const SUB_API_URL =
  (typeof window !== 'undefined' && window.FABTRK_API_URL) || 'https://api.fabtrk.com';

/** The org embed token, the same capability the quote form posts with. */
function subEmbedToken() {
  if (typeof window === 'undefined') return '';
  if (window.FABTRK_EMBED_TOKEN) return window.FABTRK_EMBED_TOKEN;
  try {
    return new URL(window.FABTRK_DESIGNER_URL).searchParams.get('org') || '';
  } catch (e) {
    return '';
  }
}

// Matched to what the FabTrk endpoint accepts, so an applicant is told here
// rather than after a long upload.
const SUB_MAX_FILES = 8;
const SUB_MAX_FILE_BYTES = 32 * 1024 * 1024;

const subReadFileAsDataUrl = (file) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = () => reject(new Error(`Could not read ${file.name}`));
    reader.readAsDataURL(file);
  });

const subFmtBytes = (n) => {
  if (!n) return '';
  if (n < 1024 * 1024) return `${Math.round(n / 1024)} KB`;
  return `${(n / (1024 * 1024)).toFixed(1)} MB`;
};

const TRADES = [
  'Concrete',
  'Metal Building Erection',
  'Framing',
  'Electrical',
  'Plumbing',
  'HVAC',
  'Site Work / Dirt',
  'Roofing',
  'Insulation',
  'Doors & Windows',
  'Painting',
  'Landscaping',
  'Drywall',
  'Other',
];

const REQUIREMENTS = [
  'Active license (if required)',
  'General liability insurance',
  'Workers comp or exemption',
  'Reliable crew and equipment',
  'Ability to meet schedules',
  'Willingness to follow site safety rules',
];

const SubHero = () => (
  <section style={{ borderBottom: '1px solid var(--border)', position: 'relative' }}>
    <div className="grid-bg" style={{
      position: 'absolute', inset: 0, opacity: 0.4,
      maskImage: 'linear-gradient(to bottom, black, transparent)',
      WebkitMaskImage: 'linear-gradient(to bottom, black, transparent)',
    }}></div>
    <div className="page" style={{ position: 'relative', paddingTop: 80, paddingBottom: 80 }}>
      <SectionEyebrow id="subcontractors" label="Subcontractor application" />
      <h1 style={{ maxWidth: 1100 }}>
        Work with us.
      </h1>
      <p style={{ marginTop: 32, maxWidth: 640, fontSize: 18 }}>
        We are always looking to build long-term relationships with reliable, licensed, and insured
        subcontractors. If you take pride in your work and value clear communication,
        we'd love to hear from you.
      </p>

      {/* Requirements inline */}
      <div className="mob-stack" style={{
        marginTop: 48, display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)',
        border: '1px solid var(--border)',
      }}>
        {REQUIREMENTS.map((r, i) => (
          <div key={r} style={{
            padding: '16px 20px',
            borderRight: (i % 3 !== 2) ? '1px solid var(--border)' : 'none',
            borderBottom: i < 3 ? '1px solid var(--border)' : 'none',
            display: 'flex', alignItems: 'center', gap: 10,
          }}>
            <svg width="14" height="14" viewBox="0 0 16 16" fill="none">
              <path d="M3 8 L7 12 L13 4" stroke="var(--accent)" strokeWidth="1.8"/>
            </svg>
            <span style={{ fontSize: 13 }}>{r}</span>
          </div>
        ))}
      </div>

      {/* The page was all copy and form fields, nothing showing the work being
          applied for. A live site with several trades on it does that better
          than the hero shot of a finished building would. */}
      <ImgSlot
        label="Crew and lift equipment working a steel frame alongside a finished concrete building"
        height={340}
        sizes="100vw"
        style={{ marginTop: 48 }}
        slug="frame-utility-plant"
      />
    </div>
  </section>
);

/**
 * Upload zone for one category of paperwork (insurance, or COI/W-9 and photos).
 * Files are read into `data:` URIs so the whole application travels as one JSON
 * POST, and each keeps its category as a `label` so the shop can tell at a
 * glance which certificate is which.
 *
 * Previously this stored `f.name` and discarded the bytes, so the applicant saw a
 * filename appear and reasonably assumed their COI had been sent. It had not.
 */
const SubFileDrop = ({ accept, label, sub, files, onFiles }) => {
  const ref = React.useRef(null);
  const labelId = React.useId();
  const [reading, setReading] = React.useState(false);
  const [fileError, setFileError] = React.useState('');

  const add = async (incoming) => {
    const list = Array.from(incoming || []);
    if (!list.length) return;
    setFileError('');
    const problems = [];
    const room = SUB_MAX_FILES - files.length;
    if (room <= 0) {
      setFileError(`That's the limit of ${SUB_MAX_FILES} files.`);
      return;
    }
    const ok = [];
    for (const file of list.slice(0, room)) {
      if (file.size > SUB_MAX_FILE_BYTES) {
        problems.push(`${file.name} is ${subFmtBytes(file.size)}. The limit is 32 MB.`);
        continue;
      }
      if (files.some((f) => f.name === file.name && f.size === file.size)) continue;
      ok.push(file);
    }
    if (list.length > room) problems.push(`Only the first ${room} were added.`);

    if (ok.length) {
      setReading(true);
      try {
        const read = await Promise.all(
          ok.map(async (file) => ({
            name: file.name,
            size: file.size,
            label,
            dataUri: await subReadFileAsDataUrl(file),
          }))
        );
        onFiles([...files, ...read]);
      } catch (err) {
        problems.push(err.message || 'One of those files could not be read.');
      } finally {
        setReading(false);
      }
    }
    if (problems.length) setFileError(problems.join(' '));
  };

  return (
    <div className="field">
      <span className="field-label" id={labelId}>{label}</span>
      <button
        type="button"
        aria-describedby={labelId}
        onClick={() => ref.current?.click()}
        onDragOver={(e) => e.preventDefault()}
        onDrop={(e) => { e.preventDefault(); add(e.dataTransfer.files); }}
        style={{
          display: 'block', width: '100%', font: 'inherit', color: 'inherit',
          border: '1px dashed var(--border-strong)', background: 'var(--bg-card)',
          padding: files.length ? 16 : 24, cursor: 'pointer', textAlign: 'center',
        }}
      >
        <input
          ref={ref}
          type="file"
          multiple
          accept={accept}
          className="visually-hidden"
          aria-label={typeof label === 'string' ? label : 'Choose files'}
          onChange={(e) => { add(e.target.files); e.target.value = ''; }}
        />
        <span style={{ display: 'block', fontSize: 13, color: 'var(--fg-dim)' }}>
          {reading ? 'Reading files…' : files.length ? 'Add another file' : 'Drop files here or click to select'}
        </span>
        <span className="mono" style={{ display: 'block', fontSize: 10, color: 'var(--fg-faint)', marginTop: 6, letterSpacing: '0.08em' }}>
          {sub}
        </span>
      </button>
      {files.length > 0 && (
        <ul style={{ listStyle: 'none', margin: '8px 0 0', padding: 0, display: 'flex', flexDirection: 'column', gap: 6 }}>
          {files.map((f, i) => (
            <li key={`${f.name}-${i}`} style={{
              display: 'flex', alignItems: 'center', gap: 10,
              border: '1px solid var(--border)', background: 'var(--bg-card)', padding: '8px 10px',
            }}>
              <svg width="16" height="16" viewBox="0 0 20 20" fill="none" aria-hidden="true" style={{ flexShrink: 0 }}><path d="M5 17 V3 H12 L17 8 V17 Z" stroke="var(--accent)" strokeWidth="1.4" /><path d="M12 3 V8 H17" stroke="var(--accent)" strokeWidth="1.4" /></svg>
              <span style={{ fontSize: 12, flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{f.name}</span>
              <span className="mono" style={{ fontSize: 10, color: 'var(--fg-faint)', flexShrink: 0 }}>{subFmtBytes(f.size)}</span>
              <button
                type="button"
                onClick={() => onFiles(files.filter((_, j) => j !== i))}
                aria-label={`Remove ${f.name}`}
                style={{ background: 'none', border: 'none', color: 'var(--fg-dim)', fontSize: 11, cursor: 'pointer', textDecoration: 'underline', flexShrink: 0 }}
              >
                Remove
              </button>
            </li>
          ))}
        </ul>
      )}
      {fileError && (
        <p className="mono" style={{ marginTop: 8, fontSize: 11, color: 'var(--warn)' }} role="alert">{fileError}</p>
      )}
    </div>
  );
};

/**
 * Map the form onto FabTrk's public application contract. The field set is
 * FabTrk's (server/src/talent/subcontractors.model.ts) and this is the only
 * place that knows how our labels correspond to it.
 *
 * The two upload zones are merged into one list; each file already carries the
 * zone's name as its `label`, so the shop still sees which is the insurance
 * certificate and which is the COI.
 */
function buildApplication(form) {
  const posInt = (v) => {
    const n = parseInt(String(v ?? '').replace(/[^\d]/g, ''), 10);
    return Number.isFinite(n) && n >= 0 ? n : undefined;
  };
  return {
    companyName: (form.companyName || '').trim(),
    contactName: (form.contactName || '').trim() || undefined,
    email: (form.email || '').trim() || undefined,
    phone: (form.phone || '').trim() || undefined,
    trades: form.trades || [],
    serviceArea: (form.serviceArea || '').trim() || undefined,
    yearsInBusiness: posInt(form.years),
    crewSize: posInt(form.crewSize),
    licenseNumber: (form.license || '').trim() || undefined,
    workersComp: (form.wc || '').trim() || undefined,
    references: (form.refs || '').trim() || undefined,
    documents: [...(form.insuranceFiles || []), ...(form.coiFiles || [])].map((f) => ({
      name: f.name,
      dataUri: f.dataUri,
      label: f.label,
    })),
  };
}

async function submitApplication(form) {
  const token = subEmbedToken();
  if (!token) throw new Error("This form isn't connected yet. Please call us at 386.688.1121.");
  const res = await fetch(
    `${SUB_API_URL}/api/v1/public/embed/${encodeURIComponent(token)}/subcontractors`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(buildApplication(form)),
    }
  );
  const json = await res.json().catch(() => ({}));
  if (!res.ok) {
    if (res.status === 429) {
      throw new Error("We've had a lot of submissions from your network. Give it a few minutes, or call 386.688.1121.");
    }
    throw new Error(json?.error?.message || "We couldn't send that. Please try again, or call 386.688.1121.");
  }
  return json?.data || {};
}

const SubcontractorForm = () => {
  const [form, setForm] = React.useState({
    trades: [],
    insuranceFiles: [],
    coiFiles: [],
  });
  const [submitted, setSubmitted] = React.useState(false);
  const [receipt, setReceipt] = React.useState(null);
  const [sending, setSending] = React.useState(false);
  const [sendError, setSendError] = React.useState('');
  const upd = (patch) => setForm(f => ({ ...f, ...patch }));
  const toggleTrade = (t) => {
    setForm(f => ({
      ...f,
      trades: f.trades.includes(t) ? f.trades.filter(x => x !== t) : [...f.trades, t],
    }));
  };

  const required = ['companyName', 'contactName', 'phone', 'email'];
  const canSubmit = required.every(k => (form[k] || '').trim().length > 0) && form.trades.length > 0;

  const send = async () => {
    if (!canSubmit || sending) return;
    setSending(true);
    setSendError('');
    try {
      const data = await submitApplication(form);
      setReceipt(data);
      setSubmitted(true);
    } catch (err) {
      setSendError(err.message || 'Something went wrong. Please try again.');
    } finally {
      setSending(false);
    }
  };

  if (submitted) {
    return (
      <section className="section">
        <div className="page" style={{ maxWidth: 720, margin: '0 auto', textAlign: 'center', padding: '80px 32px' }}>
          <div style={{
            width: 64, height: 64, margin: '0 auto',
            border: '1px solid var(--accent)', display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <svg width="28" height="28" viewBox="0 0 28 28" fill="none"><path d="M6 14 L12 20 L22 8" stroke="var(--accent)" strokeWidth="2"/></svg>
          </div>
          <h2 style={{ marginTop: 32 }}>Application received.</h2>
          <p style={{ marginTop: 16, fontSize: 18, color: 'var(--fg-dim)' }}>
            We'll review your information and follow up with you.
          </p>
          {receipt?.id && (
            <div style={{ marginTop: 32, padding: 20, border: '1px solid var(--border)', background: 'var(--bg-card)', textAlign: 'left' }}>
              <div className="mono" style={{ fontSize: 11, color: 'var(--accent)', letterSpacing: '0.08em' }}>REFERENCE</div>
              <div className="mono" style={{ marginTop: 8, fontSize: 20 }}>{receipt.id}</div>
              <p style={{ marginTop: 10, fontSize: 13, color: 'var(--fg-faint)' }}>
                Quote this if you follow up. It's the same number on our end.
                {receipt.documentsStored
                  ? ` We received ${receipt.documentsStored} file${receipt.documentsStored > 1 ? 's' : ''}.`
                  : ''}
              </p>
              {/* A refused file must never look like a delivered one, and a missing
                  COI is exactly the thing that stalls an application. */}
              {(() => {
                const sent = (form.insuranceFiles || []).length + (form.coiFiles || []).length;
                const dropped = sent - (receipt.documentsStored ?? 0);
                return dropped > 0 ? (
                  <p className="mono" style={{ marginTop: 10, fontSize: 12, color: 'var(--warn)' }}>
                    ⚠ {dropped} FILE(S) COULDN'T BE ACCEPTED , CALL US AND WE'LL TAKE THEM BY EMAIL
                  </p>
                ) : null;
              })()}
            </div>
          )}
        </div>
      </section>
    );
  }

  return (
    <section className="section">
      <div className="page">

        {/* Contact info */}
        <div className="mob-stack" style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 20 }}>
          <div className="field">
            <label htmlFor="sc-company">Company name <span className="req" aria-hidden="true">*</span></label>
            <input id="sc-company" name="companyName" required aria-required="true" autoComplete="organization" className="input" value={form.companyName || ''} onChange={e => upd({ companyName: e.target.value })} />
          </div>
          <div className="field">
            <label htmlFor="sc-contact">Contact name <span className="req" aria-hidden="true">*</span></label>
            <input id="sc-contact" name="contactName" required aria-required="true" autoComplete="name" className="input" value={form.contactName || ''} onChange={e => upd({ contactName: e.target.value })} />
          </div>
          <div className="field">
            <label htmlFor="sc-phone">Phone <span className="req" aria-hidden="true">*</span></label>
            <input id="sc-phone" name="phone" required aria-required="true" autoComplete="tel" className="input" type="tel" value={form.phone || ''} onChange={e => upd({ phone: e.target.value })} />
          </div>
          <div className="field">
            <label htmlFor="sc-email">Email <span className="req" aria-hidden="true">*</span></label>
            <input id="sc-email" name="email" required aria-required="true" autoComplete="email" className="input" type="email" value={form.email || ''} onChange={e => upd({ email: e.target.value })} />
          </div>
          <div className="field" style={{ gridColumn: 'span 2' }}>
            <label htmlFor="sc-area">Service area (counties)</label>
            <input id="sc-area" name="serviceArea" className="input" placeholder="e.g. Suwannee, Madison, Hamilton, Lafayette, Columbia" value={form.serviceArea || ''} onChange={e => upd({ serviceArea: e.target.value })} />
          </div>
        </div>

        {/* Trades */}
        {/* Multi-select: exposed as a labelled group of checkboxes, so the
            on/off state is announced instead of being carried by colour alone. */}
        <div className="field" style={{ marginTop: 32 }} role="group" aria-labelledby="sc-trades-label">
          <span className="field-label" id="sc-trades-label">Trade(s) performed <span className="req" aria-hidden="true">*</span></span>
          <div className="mob-2col" style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8, marginTop: 4 }}>
            {TRADES.map(t => {
              const on = form.trades.includes(t);
              return (
                <button
                  key={t}
                  type="button"
                  role="checkbox"
                  aria-checked={on}
                  onClick={() => toggleTrade(t)}
                  style={{
                    padding: '12px 14px',
                    background: on ? 'rgba(107,166,207,0.12)' : 'var(--bg-card)',
                    border: `1px solid ${on ? 'var(--accent)' : 'var(--border)'}`,
                    color: on ? 'var(--fg)' : 'var(--fg-dim)',
                    fontSize: 13,
                    fontFamily: 'inherit',
                    textAlign: 'left',
                    cursor: 'pointer',
                    display: 'flex', alignItems: 'center', gap: 10,
                  }}
                >
                  <span aria-hidden="true" style={{
                    width: 14, height: 14,
                    border: `1px solid ${on ? 'var(--accent)' : 'var(--border-strong)'}`,
                    display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
                  }}>
                    {on && <span style={{ width: 6, height: 6, background: 'var(--accent)' }}></span>}
                  </span>
                  {t}
                </button>
              );
            })}
          </div>
        </div>

        {/* Details */}
        <div className="mob-stack" style={{ marginTop: 32, display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 20 }}>
          <div className="field">
            <label htmlFor="sc-years">Years in business</label>
            <input id="sc-years" name="years" className="input" type="number" min="0" value={form.years || ''} onChange={e => upd({ years: e.target.value })} />
          </div>
          <div className="field">
            <label htmlFor="sc-crew">Crew size</label>
            <input id="sc-crew" name="crewSize" className="input" type="number" min="0" value={form.crewSize || ''} onChange={e => upd({ crewSize: e.target.value })} />
          </div>
          <div className="field">
            <label htmlFor="sc-license">License number</label>
            <input id="sc-license" name="license" className="input" placeholder="If applicable" value={form.license || ''} onChange={e => upd({ license: e.target.value })} />
          </div>
        </div>

        <div className="field" style={{ marginTop: 24 }}>
          <label htmlFor="sc-wc">Workers comp (or exemption)</label>
          <input id="sc-wc" name="wc" className="input" placeholder="Policy # or exemption ID" value={form.wc || ''} onChange={e => upd({ wc: e.target.value })} />
        </div>

        <div className="field" style={{ marginTop: 24 }}>
          <label htmlFor="sc-refs">References (2 to 3)</label>
          <textarea id="sc-refs" name="refs" className="textarea" rows="4" placeholder="Name, company, phone, relationship, one per line" value={form.refs || ''} onChange={e => upd({ refs: e.target.value })}></textarea>
        </div>

        {/* Uploads */}
        <div className="mob-stack" style={{ marginTop: 24, display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 20 }}>
          <SubFileDrop
            label="Insurance"
            accept=".pdf,.jpg,.jpeg,.png"
            files={form.insuranceFiles || []}
            onFiles={(files) => upd({ insuranceFiles: files })}
            sub="PDF · JPG · PNG. UP TO 32 MB EACH"
          />
          <SubFileDrop
            label="COI, W-9, and photos of work"
            accept=".pdf,.jpg,.jpeg,.png,.doc,.docx"
            files={form.coiFiles || []}
            onFiles={(files) => upd({ coiFiles: files })}
            sub="PDF · JPG · PNG · DOC. UP TO 32 MB EACH"
          />
        </div>

        {sendError && (
          <div role="alert" style={{
            marginTop: 32, padding: '14px 18px',
            border: '1px solid var(--warn)', background: 'rgba(255,180,168,0.08)',
          }}>
            <span className="mono" style={{ fontSize: 12, color: 'var(--warn)', letterSpacing: '0.04em' }}>
              ⚠ {sendError}
            </span>
          </div>
        )}

        {/* Submit */}
        <div style={{
          marginTop: 40, paddingTop: 24, borderTop: '1px solid var(--border)',
          display: 'flex', justifyContent: 'flex-end', alignItems: 'center',
        }}>
          <button
            className="btn btn-primary btn-lg"
            disabled={!canSubmit || sending}
            onClick={send}
            style={{ opacity: canSubmit && !sending ? 1 : 0.5 }}
          >
            {sending ? 'Sending…' : 'Submit application'}
            {!sending && <svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2 6 L5 9 L10 3" stroke="currentColor" strokeWidth="1.6"/></svg>}
          </button>
        </div>
      </div>
    </section>
  );
};

const Subcontractor = () => (
  <div className="page-anim">
    <SubHero />
    <SubcontractorForm />
  </div>
);

Object.assign(window, { Subcontractor });
