// tc-sign.jsx — beautiful, ink-like signature capture (szimek/signature_pad)
// + image upload. Both paths produce the SAME result: a trimmed, transparent PNG
// stored with the profile and stamped onto the timecard PDF.
//
// NOTE: the library registers window.SignaturePad — we use it, never overwrite it.
const { useRef: useRefS, useEffect: useEffectS, useState: useStateS } = React;

// Crop a canvas to the bounding box of its non-transparent pixels → transparent PNG.
function trimToTransparentPNG(canvas, pad = 8) {
  const ctx = canvas.getContext('2d');
  const { width: w, height: h } = canvas;
  if (!w || !h) return null;
  const data = ctx.getImageData(0, 0, w, h).data;
  let minX = w, minY = h, maxX = 0, maxY = 0, found = false;
  for (let y = 0; y < h; y++) {
    for (let x = 0; x < w; x++) {
      if (data[(y * w + x) * 4 + 3] > 8) {
        found = true;
        if (x < minX) minX = x; if (x > maxX) maxX = x;
        if (y < minY) minY = y; if (y > maxY) maxY = y;
      }
    }
  }
  if (!found) return null;
  minX = Math.max(0, minX - pad); minY = Math.max(0, minY - pad);
  maxX = Math.min(w - 1, maxX + pad); maxY = Math.min(h - 1, maxY + pad);
  const cw = maxX - minX + 1, ch = maxY - minY + 1;
  // Downscale so the stored PNG stays small (full-res uploads can be multi-MB,
  // which blows the localStorage quota and silently fails to persist).
  const MAXW = 560, MAXH = 200;
  const scale = Math.min(1, MAXW / cw, MAXH / ch);
  const ow = Math.max(1, Math.round(cw * scale));
  const oh = Math.max(1, Math.round(ch * scale));
  const out = document.createElement('canvas');
  out.width = ow; out.height = oh;
  const octx = out.getContext('2d');
  octx.imageSmoothingEnabled = true; octx.imageSmoothingQuality = 'high';
  octx.drawImage(canvas, minX, minY, cw, ch, 0, 0, ow, oh);
  return out.toDataURL('image/png');
}

// Knock a white / light background out to transparency, keeping the dark ink.
// Used for UPLOADED signature images (screenshots come on a solid white bg).
// Alpha is derived from luminance so anti-aliased edges stay smooth; ink color
// is preserved (near-black screenshots read black, colored ink keeps its hue).
function knockoutBackground(canvas, opts = {}) {
  const whiteThresh = opts.whiteThresh != null ? opts.whiteThresh : 232; // >= → fully transparent
  const inkThresh = opts.inkThresh != null ? opts.inkThresh : 120;       // <= → fully opaque
  const ctx = canvas.getContext('2d');
  const { width: w, height: h } = canvas;
  if (!w || !h) return;
  const imgData = ctx.getImageData(0, 0, w, h);
  const d = imgData.data;
  const span = whiteThresh - inkThresh;
  for (let i = 0; i < d.length; i += 4) {
    if (d[i + 3] === 0) continue;                 // already transparent
    const lum = 0.299 * d[i] + 0.587 * d[i + 1] + 0.114 * d[i + 2];
    let a;
    if (lum >= whiteThresh) a = 0;
    else if (lum <= inkThresh) a = 255;
    else a = Math.round((whiteThresh - lum) / span * 255);
    // combine with any existing alpha so we never make transparent pixels opaque
    d[i + 3] = Math.min(d[i + 3], a);
  }
  ctx.putImageData(imgData, 0, 0);
}

// Load an arbitrary image data URL, draw to a canvas, knock out white bg, trim → transparent PNG.
function trimImageDataURL(dataUrl) {
  return new Promise((resolve) => {
    const img = new Image();
    img.onload = () => {
      const c = document.createElement('canvas');
      c.width = img.naturalWidth; c.height = img.naturalHeight;
      c.getContext('2d').drawImage(img, 0, 0);
      try {
        knockoutBackground(c);
        resolve(trimToTransparentPNG(c, 4) || dataUrl);
      }
      catch (e) { resolve(dataUrl); }   // cross-origin / tainted → keep as-is
    };
    img.onerror = () => resolve(dataUrl);
    img.src = dataUrl;
  });
}

const THICK_MIN_RATIO = 0.6 / 2.8;   // preserve the szimek thick/thin character

function SignatureModal({ open, current, onClose, onApply }) {
  const [tab, setTab] = useStateS('draw');
  const [thickness, setThickness] = useStateS(2.8);   // == maxWidth
  const [hasInk, setHasInk] = useStateS(false);
  const [upload, setUpload] = useStateS(null);         // trimmed PNG from a file
  const canvasRef = useRefS(null);
  const padRef = useRefS(null);
  const fileRef = useRefS(null);

  // (re)build the signature_pad whenever the Draw tab is shown
  useEffectS(() => {
    if (!open || tab !== 'draw') return;
    const canvas = canvasRef.current;
    if (!canvas || !window.SignaturePad) return;

    const fitCanvas = (preserve) => {
      const ratio = Math.max(window.devicePixelRatio || 1, 1);
      const data = preserve && padRef.current ? padRef.current.toData() : null;
      canvas.width = canvas.offsetWidth * ratio;
      canvas.height = canvas.offsetHeight * ratio;
      canvas.getContext('2d').scale(ratio, ratio);
      if (padRef.current) {
        padRef.current.clear();
        if (data) padRef.current.fromData(data);
      }
    };

    const pad = new window.SignaturePad(canvas, {
      penColor: '#111',
      minWidth: thickness * THICK_MIN_RATIO,
      maxWidth: thickness,
      velocityFilterWeight: 0.7,
      backgroundColor: 'rgba(0,0,0,0)',
    });
    padRef.current = pad;
    pad.addEventListener('beginStroke', () => setHasInk(true));
    pad.addEventListener('endStroke', () => setHasInk(!pad.isEmpty()));
    fitCanvas(false);

    const onResize = () => fitCanvas(true);
    window.addEventListener('resize', onResize);
    return () => {
      window.removeEventListener('resize', onResize);
      pad.off();
      padRef.current = null;
    };
  }, [open, tab]);

  // live thickness updates
  useEffectS(() => {
    const pad = padRef.current;
    if (pad) { pad.maxWidth = thickness; pad.minWidth = Math.max(0.3, thickness * THICK_MIN_RATIO); }
  }, [thickness]);

  // reset transient state when reopened
  useEffectS(() => { if (open) { setTab('draw'); setUpload(null); setHasInk(false); } }, [open]);

  const clear = () => { if (padRef.current) { padRef.current.clear(); setHasInk(false); } };
  const undo = () => {
    const pad = padRef.current; if (!pad) return;
    const data = pad.toData();
    if (data.length) { data.pop(); pad.fromData(data); setHasInk(data.length > 0); }
  };

  const onFile = (e) => {
    const f = e.target.files && e.target.files[0];
    if (!f) return;
    const r = new FileReader();
    r.onload = async () => setUpload(await trimImageDataURL(r.result));
    r.readAsDataURL(f);
  };

  const canSave = tab === 'draw' ? hasInk : !!upload;
  const save = () => {
    let url = null;
    if (tab === 'draw' && padRef.current && !padRef.current.isEmpty()) {
      url = trimToTransparentPNG(padRef.current.canvas, 8);
    } else if (tab === 'upload' && upload) {
      url = upload;
    }
    if (url) { onApply(url); onClose(); }
  };

  return (
    <Modal open={open} onClose={onClose} title="Signature" width={580}
      footer={<>
        <span className="muted sm">Saved with your profile · stamped on the PDF.</span>
        <Button variant="primary" onClick={save} disabled={!canSave}>Save signature</Button>
      </>}>
      <div className="seg sig-tabs">
        <button className={'seg-btn' + (tab === 'draw' ? ' on' : '')} onClick={() => setTab('draw')}>Draw</button>
        <button className={'seg-btn' + (tab === 'upload' ? ' on' : '')} onClick={() => setTab('upload')}>Upload</button>
      </div>

      {tab === 'draw' ? (
        <div className="sigpad">
          <canvas ref={canvasRef} className="sigpad-canvas"></canvas>
          <div className="sigpad-base">Sign here</div>
          <div className="sigpad-bar">
            <div className="sigpad-actions">
              <Button variant="ghost" size="sm" onClick={undo} disabled={!hasInk}>Undo</Button>
              <Button variant="ghost" size="sm" onClick={clear} disabled={!hasInk}>Clear</Button>
            </div>
            <label className="thick-slider">
              <span className="muted sm">Pen</span>
              <input type="range" min="1.5" max="4.5" step="0.1" value={thickness}
                onChange={(e) => setThickness(parseFloat(e.target.value))} />
            </label>
          </div>
        </div>
      ) : (
        <div className="sig-uploadbox">
          <input ref={fileRef} type="file" accept="image/*" hidden onChange={onFile} />
          {upload ? (
            <div className="sig-draft"><img src={upload} alt="uploaded signature" /></div>
          ) : (
            <div className="sig-drop" onClick={() => fileRef.current && fileRef.current.click()}>
              <span>Choose a signature image…</span>
              <span className="muted sm">PNG / JPG — margins are trimmed automatically</span>
            </div>
          )}
          {upload && <button className="link-btn" onClick={() => fileRef.current && fileRef.current.click()}>Choose a different image</button>}
        </div>
      )}

      {current && (
        <div className="sig-current">
          <span className="muted sm">Current:</span>
          <img src={current} alt="current signature" />
        </div>
      )}
    </Modal>
  );
}

Object.assign(window, { SignatureModal, trimToTransparentPNG, trimImageDataURL, knockoutBackground });
