/* global React, ReactDOM */

// v07zz22 — Blocking first-login profile-completion modal. Renders
// over everything (via #modal-root portal) when the authenticated
// user's must_complete_profile flag is 1. Collects:
//   - New password (+ confirm) → replaces the temp invite password
//   - Full name
//   - Company
//   - Role title (dropdown matching the admin role enum)
//   - Avatar upload (optional) with a draggable square crop for the
//     small thumbnail variant. The full image becomes the portrait
//     card (3:4, CSS cover) and the user-selected square region
//     becomes the square thumbnail used in chips / avatar circles.

// v07zz23 — Role assignment removed from the user-facing form per
// Hugo: the admin is the only one who decides someone's role at
// invite time. The role is still displayed on the modal as a
// read-only chip so the user sees what they were assigned, but
// they can't change it. The server endpoint accepts no role_title
// from this form anymore.
const ROLE_DISPLAY = {
  admin: "Admin", producer: "Producer", director: "Director",
  supervisor: "Supervisor", lead: "Lead", artist: "Artist",
  reviewer: "Reviewer", tester: "Tester",
};

// v07zz26 — IANA timezone list. Tries Intl.supportedValuesOf
// ("timeZone") first (modern browsers; ~400 entries), falls
// back to a hand-picked shortlist if the browser doesn't
// support it.
function getTimezoneList() {
  try {
    if (typeof Intl !== "undefined" && typeof Intl.supportedValuesOf === "function") {
      const all = Intl.supportedValuesOf("timeZone");
      if (Array.isArray(all) && all.length) return all;
    }
  } catch (_) {}
  return [
    "UTC", "Europe/London", "Europe/Paris", "Europe/Berlin",
    "Europe/Madrid", "Europe/Vienna", "Europe/Rome", "Europe/Amsterdam",
    "Europe/Stockholm", "Europe/Athens", "Europe/Istanbul", "Europe/Moscow",
    "Africa/Cairo", "Africa/Johannesburg",
    "Asia/Dubai", "Asia/Karachi", "Asia/Kolkata", "Asia/Bangkok",
    "Asia/Singapore", "Asia/Hong_Kong", "Asia/Shanghai", "Asia/Tokyo",
    "Asia/Seoul",
    "Australia/Sydney", "Australia/Melbourne", "Australia/Perth",
    "Pacific/Auckland",
    "America/New_York", "America/Toronto", "America/Chicago",
    "America/Denver", "America/Los_Angeles", "America/Vancouver",
    "America/Mexico_City", "America/Bogota", "America/Lima",
    "America/Buenos_Aires", "America/Sao_Paulo",
  ];
}
function detectBrowserTimezone() {
  try {
    return (Intl.DateTimeFormat().resolvedOptions().timeZone) || "UTC";
  } catch (_) { return "UTC"; }
}

function CompleteProfileModal({ user, onComplete }) {
  const fetcher = window.authFetch || fetch;
  const [name, setName]           = React.useState(user.name || "");
  const [company, setCompany]     = React.useState(user.company || "");
  const [bio, setBio]             = React.useState(user.bio || "");
  const [tz, setTz]               = React.useState(user.tz || detectBrowserTimezone());
  const [pwd1, setPwd1]           = React.useState("");
  const [pwd2, setPwd2]           = React.useState("");
  const tzList = React.useMemo(getTimezoneList, []);
  const [imageSrc, setImageSrc]   = React.useState(null);   // dataURL of selected file
  const [imageDims, setImageDims] = React.useState(null);   // {w, h}
  const [cropRect, setCropRect]   = React.useState(null);   // {x, y, size}  in image-pixel coordinates
  const [busy, setBusy]           = React.useState(false);
  const [err, setErr]             = React.useState(null);

  // ── File picker
  const fileRef = React.useRef(null);
  const onPickFile = (file) => {
    if (!file) return;
    if (!/^image\//.test(file.type)) {
      setErr("File must be an image.");
      return;
    }
    setErr(null);
    const reader = new FileReader();
    reader.onload = () => {
      const img = new Image();
      img.onload = () => {
        setImageSrc(reader.result);
        setImageDims({ w: img.naturalWidth, h: img.naturalHeight });
        // Default square crop = a centred square as large as the
        // shorter dimension. User can drag to recompose.
        const size = Math.min(img.naturalWidth, img.naturalHeight);
        setCropRect({
          x: Math.floor((img.naturalWidth  - size) / 2),
          y: Math.floor((img.naturalHeight - size) / 2),
          size,
        });
      };
      img.src = reader.result;
    };
    reader.readAsDataURL(file);
  };

  // ── Crop region drag handlers (operate in image-pixel coordinates,
  //    rendered as a percentage overlay on the preview).
  const cropDragRef = React.useRef(null);
  const previewWrapRef = React.useRef(null);
  const onCropMouseDown = (e) => {
    if (!cropRect || !imageDims || !previewWrapRef.current) return;
    e.preventDefault();
    const rect = previewWrapRef.current.getBoundingClientRect();
    cropDragRef.current = {
      startX: e.clientX,
      startY: e.clientY,
      origX: cropRect.x,
      origY: cropRect.y,
      pxPerImgX: rect.width  / imageDims.w,
      pxPerImgY: rect.height / imageDims.h,
    };
    const onMove = (ev) => {
      const d = cropDragRef.current;
      if (!d) return;
      const dxImg = (ev.clientX - d.startX) / d.pxPerImgX;
      const dyImg = (ev.clientY - d.startY) / d.pxPerImgY;
      const nx = Math.max(0, Math.min(imageDims.w - cropRect.size, Math.round(d.origX + dxImg)));
      const ny = Math.max(0, Math.min(imageDims.h - cropRect.size, Math.round(d.origY + dyImg)));
      setCropRect(prev => ({ ...prev, x: nx, y: ny }));
    };
    const onUp = () => {
      cropDragRef.current = null;
      document.removeEventListener("mousemove", onMove);
      document.removeEventListener("mouseup", onUp);
    };
    document.addEventListener("mousemove", onMove);
    document.addEventListener("mouseup", onUp);
  };
  const onResizeCropSize = (newSize) => {
    if (!cropRect || !imageDims) return;
    const cappedSize = Math.max(40, Math.min(Math.min(imageDims.w, imageDims.h), Math.round(newSize)));
    const x = Math.max(0, Math.min(imageDims.w - cappedSize, cropRect.x));
    const y = Math.max(0, Math.min(imageDims.h - cappedSize, cropRect.y));
    setCropRect({ x, y, size: cappedSize });
  };

  // ── Render a canvas-cropped square blob for upload
  const cropToBlob = (src, rect, outSize, type) => new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => {
      try {
        const c = document.createElement("canvas");
        c.width  = outSize;
        c.height = outSize;
        const ctx = c.getContext("2d");
        ctx.drawImage(img, rect.x, rect.y, rect.size, rect.size, 0, 0, outSize, outSize);
        c.toBlob(b => b ? resolve(b) : reject(new Error("canvas.toBlob returned null")), type || "image/png", 0.92);
      } catch (e) { reject(e); }
    };
    img.onerror = () => reject(new Error("image failed to load"));
    img.src = src;
  });

  // ── Submit
  const onSubmit = async () => {
    if (busy) return;
    setErr(null);
    if (String(pwd1).length < 8) { setErr("Password must be at least 8 characters."); return; }
    if (pwd1 !== pwd2)            { setErr("Passwords don't match.");                 return; }
    if (!name.trim())             { setErr("Name is required.");                       return; }
    if (!company.trim())          { setErr("Company is required.");                    return; }
    setBusy(true);
    try {
      if (imageSrc && imageDims && cropRect) {
        // v07zz140 — Upload the avatar through the dedicated
        // /api/users/me/avatar endpoint (direct-to-R2, no activity log,
        // no asset_references row). The old path posted to /api/upload
        // as a "reference", which polluted the External References list
        // and fired a "reference uploaded" notification for what was
        // really just a profile photo. Best-effort: if storage isn't
        // configured, skip the photo and let the rest of the profile save.
        try {
          const portraitBlob = await (async () => {
            const img = new Image();
            await new Promise((res, rej) => { img.onload = res; img.onerror = rej; img.src = imageSrc; });
            const maxW = 800;
            const scale = Math.min(1, maxW / img.naturalWidth);
            const w = Math.round(img.naturalWidth  * scale);
            const h = Math.round(img.naturalHeight * scale);
            const c = document.createElement("canvas");
            c.width = w; c.height = h;
            c.getContext("2d").drawImage(img, 0, 0, w, h);
            return await new Promise((res, rej) => c.toBlob(b => b ? res(b) : rej(new Error("portrait toBlob null")), "image/png", 0.92));
          })();
          const squareBlob = await cropToBlob(imageSrc, cropRect, 512, "image/png");
          const fd = new FormData();
          fd.append("portrait", portraitBlob, "portrait.png");
          fd.append("square",   squareBlob,   "square.png");
          await fetcher("/api/users/me/avatar", { method: "POST", body: fd });
        } catch (e) {
          console.warn("[profile] avatar upload skipped:", e.message);
          setErr(`Avatar upload skipped: ${e.message}. The rest of your profile was saved — you can add the photo later from Profile & Account.`);
        }
      }
      const r = await fetcher("/api/auth/complete-profile", {
        method: "POST",
        body: JSON.stringify({
          new_password: pwd1,
          name, company,
          bio: bio ? String(bio).trim() : null,
          tz: tz || null,
          // Avatar already saved via /api/users/me/avatar above.
        }),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(j.error || `HTTP ${r.status}`);
      onComplete && onComplete(j.user);
    } catch (e) {
      setErr(e.message);
    } finally {
      setBusy(false);
    }
  };

  const onDrop = (e) => {
    e.preventDefault();
    const file = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0];
    if (file) onPickFile(file);
  };

  const cropPctStyle = (cropRect && imageDims) ? {
    left:   `${(cropRect.x / imageDims.w) * 100}%`,
    top:    `${(cropRect.y / imageDims.h) * 100}%`,
    width:  `${(cropRect.size / imageDims.w) * 100}%`,
    height: `${(cropRect.size / imageDims.h) * 100}%`,
  } : null;

  return ReactDOM.createPortal((
    <div className="cpm-backdrop" role="dialog" aria-modal="true" aria-label="Complete your profile">
      <div className="cpm-card glass">
        <header className="cpm-head">
          <div className="cpm-eyebrow">FIRST-TIME SETUP</div>
          <div className="cpm-title">Welcome — finish your profile</div>
          <div className="cpm-sub">Choose a new password and tell us a bit about you. This screen unlocks once everything's filled in.</div>
        </header>
        <div className="cpm-body">
          <div className="cpm-col cpm-col--form">
            <label className="cpm-field">
              <span className="cpm-label">New password</span>
              <input type="password" autoComplete="new-password" value={pwd1} onChange={e => setPwd1(e.target.value)} placeholder="Min 8 characters"/>
            </label>
            <label className="cpm-field">
              <span className="cpm-label">Confirm password</span>
              <input type="password" autoComplete="new-password" value={pwd2} onChange={e => setPwd2(e.target.value)} placeholder="Repeat"/>
            </label>
            <label className="cpm-field">
              <span className="cpm-label">Full name</span>
              <input type="text" value={name} onChange={e => setName(e.target.value)} placeholder="Jane Smith"/>
            </label>
            <label className="cpm-field">
              <span className="cpm-label">Company</span>
              <input type="text" value={company} onChange={e => setCompany(e.target.value)} placeholder="Acme VFX"/>
            </label>
            <div className="cpm-field">
              <span className="cpm-label">Your role</span>
              <div className="cpm-role-chip">{ROLE_DISPLAY[user.role] || user.role || "Artist"}</div>
              <span className="cpm-help">Assigned by your admin. Reach out if it's not right.</span>
            </div>
            <label className="cpm-field">
              <span className="cpm-label">Timezone</span>
              <select value={tz} onChange={e => setTz(e.target.value)}>
                {tzList.map(z => (
                  <option key={z} value={z}>{z.replace(/_/g, " ")}</option>
                ))}
              </select>
              <span className="cpm-help">Detected from your browser. Change if it's off.</span>
            </label>
            <label className="cpm-field">
              <span className="cpm-label">Short bio (optional)</span>
              <textarea
                rows={2}
                value={bio}
                onChange={e => setBio(e.target.value.slice(0, 200))}
                placeholder="One-liner shown on your crew card. e.g. “Owns asset pipeline + archive ingest.”"
                style={{
                  font: "inherit", fontSize: "var(--fs-13-5)", padding: "9px 12px",
                  borderRadius: "var(--r-7)", border: "1px solid color-mix(in srgb, var(--card-border) 55%, transparent)",
                  background: "color-mix(in srgb, var(--cream) 85%, transparent)", color: "var(--ink)",
                  outline: "none", resize: "vertical",
                }}
              />
              <span className="cpm-help">{200 - (bio || "").length} characters left.</span>
            </label>
          </div>
          <div className="cpm-col cpm-col--avatar">
            <div className="cpm-label">Avatar (optional)</div>
            {!imageSrc && (
              <div
                className="cpm-drop"
                onDragOver={e => e.preventDefault()}
                onDrop={onDrop}
                onClick={() => fileRef.current && fileRef.current.click()}
              >
                <div className="cpm-drop-inner">
                  <div className="cpm-drop-glyph">+</div>
                  <div className="cpm-drop-text">Drop an image, or click to pick one</div>
                  <div className="cpm-drop-hint">JPG / PNG · used for the Crew card + small chips</div>
                </div>
                <input
                  ref={fileRef}
                  type="file"
                  accept="image/*"
                  style={{ display: "none" }}
                  onChange={e => onPickFile(e.target.files && e.target.files[0])}
                />
              </div>
            )}
            {imageSrc && imageDims && cropRect && (
              <>
                <div className="cpm-preview-row">
                  <div className="cpm-preview cpm-preview--portrait">
                    <div className="cpm-preview-cap">CREW CARD (3:4)</div>
                    <div className="cpm-preview-frame" style={{ backgroundImage: `url(${imageSrc})` }}/>
                  </div>
                  <div className="cpm-preview cpm-preview--square">
                    <div className="cpm-preview-cap">SQUARE (drag the box to reframe)</div>
                    <div className="cpm-preview-frame" ref={previewWrapRef}>
                      <img src={imageSrc} alt="" draggable={false} className="cpm-preview-img"/>
                      <div
                        className="cpm-crop"
                        style={cropPctStyle}
                        onMouseDown={onCropMouseDown}
                      />
                    </div>
                  </div>
                </div>
                <div className="cpm-crop-controls">
                  <label className="cpm-crop-zoom">
                    <span className="cpm-label" style={{ fontSize: "var(--fs-10)" }}>Zoom</span>
                    <input
                      type="range"
                      min={Math.max(40, Math.round(Math.min(imageDims.w, imageDims.h) * 0.2))}
                      max={Math.min(imageDims.w, imageDims.h)}
                      value={cropRect.size}
                      onChange={e => onResizeCropSize(parseInt(e.target.value, 10))}
                    />
                  </label>
                  <button type="button" className="cpm-btn-ghost" onClick={() => { setImageSrc(null); setCropRect(null); setImageDims(null); }}>
                    Pick a different image
                  </button>
                </div>
              </>
            )}
          </div>
        </div>
        {err && <div className="cpm-error">⚠ {err}</div>}
        <footer className="cpm-foot">
          <button type="button" className="cpm-btn-primary" onClick={onSubmit} disabled={busy}>
            {busy ? "Saving…" : "Save & continue"}
          </button>
        </footer>
      </div>
    </div>
  ), document.getElementById("modal-root") || document.body);
}

if (typeof window !== "undefined") window.CompleteProfileModal = CompleteProfileModal;
