// IndieVertical - account & profile settings.
//
// Four tabs under /account: Profile (public-facing fields + avatar, autosaved),
// Account & security (email, password, sessions), Notifications (email prefs),
// and Danger zone (role switch + account deletion). Built on the same Supabase
// patterns as the project editor (debounced autosave, owner-folder Storage
// uploads) and the Marquee form components (su-card / f-row / f-input / btn).

const IV_ACCOUNT_TABS = [
  { key: "profile",       label: "Profile",            path: "/account" },
  { key: "security",      label: "Account & security", path: "/account/security" },
  { key: "notifications", label: "Notifications",      path: "/account/notifications" },
  { key: "danger",        label: "Danger zone",        path: "/account/danger" },
];

// Social link fields shown on the profile tab. Stored together in profiles.socials.
const IV_SOCIAL_FIELDS = [
  { key: "twitter",  label: "X / Twitter", placeholder: "x.com/yourstudio" },
  { key: "bluesky",  label: "Bluesky",     placeholder: "yourstudio.bsky.social" },
  { key: "discord",  label: "Discord",     placeholder: "discord.gg/invite" },
  { key: "youtube",  label: "YouTube",     placeholder: "youtube.com/@yourstudio" },
];

// System avatars: ready-made images under /avatars users can pick without
// uploading. Stored in profiles.avatar_url like any other image URL.
const IV_AVATAR_PRESETS = [
  "turtle", "robot", "cat", "fox", "mushroom", "ghost", "star", "moon",
].map((name) => "/avatars/" + name + ".png?v=2");

// Process a chosen image entirely in the browser before upload: center-crop to a
// square and downscale to 512px PNG. This keeps avatars square (good for the
// circular display), makes the upload tiny (well under the bucket size limit),
// and produces a predictable image/png content type — sidestepping the size /
// mime rejections that were failing raw uploads.
function ivProcessAvatarFile(file) {
  return new Promise((resolve, reject) => {
    const url = URL.createObjectURL(file);
    const img = new Image();
    img.onload = () => {
      URL.revokeObjectURL(url);
      const SIZE = 512;
      const side = Math.min(img.width, img.height);
      const sx = (img.width - side) / 2;
      const sy = (img.height - side) / 2;
      const canvas = document.createElement("canvas");
      canvas.width = SIZE;
      canvas.height = SIZE;
      const ctx = canvas.getContext("2d");
      ctx.drawImage(img, sx, sy, side, side, 0, 0, SIZE, SIZE);
      canvas.toBlob((blob) => (blob ? resolve(blob) : reject(new Error("encode failed"))), "image/png");
    };
    img.onerror = () => { URL.revokeObjectURL(url); reject(new Error("load failed")); };
    img.src = url;
  });
}

// ---------- shared bits ----------
function IvAccountNav({ tab }) {
  return (
    <nav className="acct-tabs" aria-label="Settings sections">
      {IV_ACCOUNT_TABS.map((t) => {
        const active = tab === t.key;
        return (
          <a
            key={t.key}
            href={t.path}
            aria-current={active ? "page" : undefined}
            className={"acct-tab" + (active ? " on" : "")}
          >
            {t.label}
          </a>
        );
      })}
    </nav>
  );
}

// Small on/off switch styled to match the Marquee buttons.
function IvToggle({ on, onChange, labelOn = "On", labelOff = "Off", disabled }) {
  return (
    <button
      type="button"
      role="switch"
      aria-checked={on}
      disabled={disabled}
      onClick={() => onChange(!on)}
      className={"vb" + (on ? " on" : "")}
      style={{ minWidth: 64 }}
    >
      {on ? labelOn : labelOff}
    </button>
  );
}

// ---------- profile tab ----------
function IvAccountProfile() {
  const { state, api } = useStore();
  const profile = state.profile || {};
  const role = state.role;

  // Local mirror for instant feedback; autosave persists with a debounce.
  const [form, setForm] = React.useState(() => ({
    display_name: profile.display_name || "",
    studio_name:  profile.studio_name || "",
    org_name:     profile.org_name || "",
    location:     profile.location || "",
    bio:          profile.bio || "",
    socials:      profile.socials || {},
  }));
  const [status, setStatus] = React.useState("idle"); // idle | saving | saved | error
  const [uploading, setUploading] = React.useState(false);
  const debounceRef = React.useRef(null);
  const pendingRef = React.useRef({}); // merged patch awaiting the next debounced flush
  const fileRef = React.useRef(null);

  // Resync if the underlying profile identity changes (e.g. after refresh).
  React.useEffect(() => {
    setForm({
      display_name: profile.display_name || "",
      studio_name:  profile.studio_name || "",
      org_name:     profile.org_name || "",
      location:     profile.location || "",
      bio:          profile.bio || "",
      socials:      profile.socials || {},
    });
  }, [profile.id]);

  function saveField(patch) {
    setForm((f) => ({ ...f, ...patch }));
    // Accumulate across rapid edits to different fields so the debounce never
    // drops an earlier change by only persisting the latest single patch.
    pendingRef.current = { ...pendingRef.current, ...patch };
    setStatus("saving");
    clearTimeout(debounceRef.current);
    debounceRef.current = setTimeout(async () => {
      const toSave = pendingRef.current;
      pendingRef.current = {};
      const ok = await api.updateProfile(toSave);
      setStatus(ok ? "saved" : "error");
    }, 600);
  }

  const set = (col) => (e) => saveField({ [col]: e.target.value });

  const setSocial = (key) => (e) => {
    const socials = { ...(form.socials || {}), [key]: e.target.value };
    saveField({ socials });
  };

  const onAvatarFile = (e) => {
    const file = e.target.files[0];
    if (!file) return;
    if (!/^image\//.test(file.type || "")) {
      api.showToast("Please choose an image file.");
      e.target.value = "";
      return;
    }
    if (file.size > 15 * 1024 * 1024) {
      api.showToast("That image is very large - please use one under 15 MB.");
      e.target.value = "";
      return;
    }
    const uid = state.session && state.session.user && state.session.user.id;
    if (!uid) { api.showToast("Your session has expired. Please log in again."); e.target.value = ""; return; }
    (async () => {
      setUploading(true);
      let blob;
      try {
        blob = await ivProcessAvatarFile(file);
      } catch (err) {
        setUploading(false);
        api.showToast("Couldn't read that image - try a different file.");
        e.target.value = "";
        return;
      }
      // Unique filename, so a plain insert (no upsert) is enough.
      const path = uid + "/avatar-" + Date.now() + ".png";
      const { error: upErr } = await SUPA.storage.from("avatars")
        .upload(path, blob, { contentType: "image/png" });
      if (upErr) {
        setUploading(false);
        api.showToast("Could not upload the image - please try again.");
        e.target.value = "";
        return;
      }
      const { data: pub } = SUPA.storage.from("avatars").getPublicUrl(path);
      const url = (pub && pub.publicUrl) || "";
      await api.updateProfile({ avatar_url: url });
      setUploading(false);
      e.target.value = "";
    })();
  };

  const onAvatarRemove = async () => {
    await api.updateProfile({ avatar_url: null });
  };

  // Pick one of the ready-made system avatars.
  const chooseAvatar = async (uri) => {
    await api.updateProfile({ avatar_url: uri });
  };

  const avatarUrl = profile.avatar_url;
  // A Google-supplied avatar URL can 404; fall back to initials if it fails.
  const [avatarBroken, setAvatarBroken] = React.useState(false);
  React.useEffect(() => setAvatarBroken(false), [avatarUrl]);
  const showAvatar = avatarUrl && !avatarBroken;
  const initials = (form.display_name || "You").trim().split(/\s+/).map((w) => w[0]).join("").slice(0, 2).toUpperCase() || "?";
  const statusLabel = status === "saving" ? "Saving…" : status === "saved" ? "Saved" : status === "error" ? "Couldn't save - check your connection" : "";

  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 12 }}>
        <p className="login-note" style={{ margin: "0 0 18px" }}>
          This is how publishers and developers see you across IndieVertical.
        </p>
        <span aria-live="polite" style={{ fontSize: 12, color: status === "error" ? "#c0392b" : "var(--fg2)" }}>{statusLabel}</span>
      </div>

      {/* Avatar */}
      <div className="f-row" style={{ display: "flex", alignItems: "center", gap: 18 }}>
        {showAvatar
          ? <img src={avatarUrl} onError={() => setAvatarBroken(true)} alt="Your avatar" style={{ width: 96, height: 96, borderRadius: "50%", objectFit: "cover", border: "2px solid var(--ink)" }} />
          : <span style={{ width: 96, height: 96, borderRadius: "50%", background: "var(--ink)", color: "var(--bg)", display: "inline-flex", alignItems: "center", justifyContent: "center", fontSize: 32, fontWeight: 800 }}>{initials}</span>}
        <div>
          <input ref={fileRef} type="file" accept="image/*" style={{ display: "none" }} onChange={onAvatarFile} />
          <button type="button" className="btn btn-secondary" disabled={uploading} onClick={() => fileRef.current && fileRef.current.click()}>
            {uploading ? "Uploading…" : "Upload a photo"}
          </button>
          {avatarUrl && <button type="button" className="btn btn-ghost" style={{ marginLeft: 8 }} onClick={onAvatarRemove}>Remove</button>}
          <div className="login-note" style={{ marginTop: 6 }}>PNG, JPG, WEBP or GIF, up to 5 MB.</div>
        </div>
      </div>

      {/* System avatars */}
      <div className="f-row">
        <span className="f-label">Or pick a system avatar</span>
        <div className="avatar-picker">
          {IV_AVATAR_PRESETS.map((uri) => (
            <button
              key={uri}
              type="button"
              className={"avatar-opt" + (avatarUrl === uri ? " on" : "")}
              aria-label="Use this avatar"
              aria-pressed={avatarUrl === uri}
              onClick={() => chooseAvatar(uri)}
            >
              <img src={uri} alt="" />
            </button>
          ))}
        </div>
      </div>

      <div className="f-row">
        <label className="f-label" htmlFor="ac-name">Display name</label>
        <input id="ac-name" className="f-input" type="text" maxLength={60} value={form.display_name} placeholder="Your name" onChange={set("display_name")} />
      </div>

      {role === "scout" ? (
        <div className="f-row">
          <label className="f-label" htmlFor="ac-org">Organization</label>
          <input id="ac-org" className="f-input" type="text" value={form.org_name} placeholder="Your publishing org" onChange={set("org_name")} />
        </div>
      ) : (
        <div className="f-row">
          <label className="f-label" htmlFor="ac-studio">Studio</label>
          <input id="ac-studio" className="f-input" type="text" value={form.studio_name} placeholder="Your studio name" onChange={set("studio_name")} />
        </div>
      )}

      <div className="f-row">
        <label className="f-label" htmlFor="ac-location">Location</label>
        <input id="ac-location" className="f-input" type="text" value={form.location} placeholder="City, Country" onChange={set("location")} />
      </div>

      <div className="f-row">
        <label className="f-label" htmlFor="ac-bio">Bio</label>
        <textarea id="ac-bio" className="f-input area" maxLength={280} value={form.bio} placeholder="A sentence or two about you or your studio." onChange={set("bio")}></textarea>
        <div className="login-note" style={{ textAlign: "right" }}>{(form.bio || "").length}/280</div>
      </div>

      <div className="su-q" style={{ marginTop: 20, marginBottom: 10 }}>Social links</div>
      {IV_SOCIAL_FIELDS.map((s) => (
        <div className="f-row" key={s.key}>
          <label className="f-label" htmlFor={"ac-" + s.key}>{s.label}</label>
          <input id={"ac-" + s.key} className="f-input" type="text" value={(form.socials && form.socials[s.key]) || ""} placeholder={s.placeholder} onChange={setSocial(s.key)} />
        </div>
      ))}
    </div>
  );
}

// ---------- account & security tab ----------
function IvAccountSecurity() {
  const { state, api } = useStore();
  const user = state.session && state.session.user;
  const currentEmail = (state.profile && state.profile.email) || (user && user.email) || "";

  // Providers the account can sign in with (e.g. ["email"], ["google"], or both).
  const providers = (user && user.app_metadata && user.app_metadata.providers) || (user && user.app_metadata && user.app_metadata.provider ? [user.app_metadata.provider] : []);
  const hasPassword = providers.indexOf("email") !== -1;
  const hasGoogle = providers.indexOf("google") !== -1;

  // Password change
  const [pw, setPw] = React.useState("");
  const [pw2, setPw2] = React.useState("");
  const [pwMsg, setPwMsg] = React.useState("");
  const [pwErr, setPwErr] = React.useState("");
  const [pwBusy, setPwBusy] = React.useState(false);

  const submitPassword = async (e) => {
    e.preventDefault();
    setPwMsg(""); setPwErr("");
    if (pw.length < 8) { setPwErr("Password must be at least 8 characters."); return; }
    if (pw !== pw2) { setPwErr("Passwords do not match."); return; }
    setPwBusy(true);
    try {
      await api.updatePassword(pw);
      setPwMsg("Password updated.");
      setPw(""); setPw2("");
    } catch (err) {
      setPwErr(err && err.message ? err.message : "Could not update the password.");
    } finally {
      setPwBusy(false);
    }
  };

  const sendReset = async () => {
    setPwMsg(""); setPwErr("");
    try {
      const { error } = await SUPA.auth.resetPasswordForEmail(currentEmail);
      if (error) throw error;
      setPwMsg("Reset link sent to " + currentEmail + ".");
    } catch (err) {
      setPwErr(err && err.message ? err.message : "Could not send a reset link.");
    }
  };

  const signOutEverywhere = () => {
    if (window.confirm("Sign out of IndieVertical on all devices?")) api.signOutEverywhere();
  };

  return (
    <div>
      {/* Email (display only) */}
      <h2 style={{ marginTop: 0 }}>Login email</h2>
      <p className="login-note" style={{ marginBottom: 4 }}>You sign in with this email:</p>
      <p style={{ fontWeight: 700, margin: 0 }}>{currentEmail || "unknown"}</p>
      <p className="login-note" style={{ marginTop: 6 }}>Need to change it? Email us at <a href="mailto:contact@indievertical.com">contact@indievertical.com</a>.</p>

      {/* Password */}
      <hr style={{ border: "none", borderTop: "1px solid #ddd", margin: "28px 0" }} />
      <h2>Password</h2>
      {hasPassword ? (
        <form onSubmit={submitPassword} noValidate>
          <div className="f-row">
            <label className="f-label" htmlFor="se-pw">New password</label>
            <input id="se-pw" className="f-input" type="password" value={pw} placeholder="At least 8 characters" disabled={pwBusy} onChange={(e) => setPw(e.target.value)} autoComplete="new-password" />
          </div>
          <div className="f-row">
            <label className="f-label" htmlFor="se-pw2">Confirm new password</label>
            <input id="se-pw2" className="f-input" type="password" value={pw2} placeholder="Re-enter password" disabled={pwBusy} onChange={(e) => setPw2(e.target.value)} autoComplete="new-password" />
          </div>
          {pwMsg && <p style={{ color: "#27ae60", fontSize: 13, margin: "8px 0 0" }}>{pwMsg}</p>}
          {pwErr && <p style={{ color: "#c0392b", fontSize: 13, margin: "8px 0 0" }}>{pwErr}</p>}
          <button type="submit" className="btn btn-primary" style={{ marginTop: 12 }} disabled={pwBusy}>{pwBusy ? "…" : "Update password"}</button>
        </form>
      ) : (
        <div>
          <p className="login-note">You sign in with Google, so there's no password to change here.</p>
          {currentEmail && (
            <React.Fragment>
              <button type="button" className="btn btn-secondary" onClick={sendReset}>Email me a reset link instead</button>
              {pwMsg && <p style={{ color: "#27ae60", fontSize: 13, margin: "8px 0 0" }}>{pwMsg}</p>}
              {pwErr && <p style={{ color: "#c0392b", fontSize: 13, margin: "8px 0 0" }}>{pwErr}</p>}
            </React.Fragment>
          )}
        </div>
      )}

      {/* Connected + sessions */}
      <hr style={{ border: "none", borderTop: "1px solid #ddd", margin: "28px 0" }} />
      <h2>Connected accounts</h2>
      <p className="login-note" style={{ marginBottom: 4 }}>
        Email/password: <strong>{hasPassword ? "connected" : "not set"}</strong>
      </p>
      <p className="login-note">Google: <strong>{hasGoogle ? "connected" : "not connected"}</strong></p>

      <hr style={{ border: "none", borderTop: "1px solid #ddd", margin: "28px 0" }} />
      <h2>Sessions</h2>
      <button type="button" className="btn btn-secondary" onClick={signOutEverywhere}>Sign out of all devices</button>
    </div>
  );
}

// ---------- notifications tab ----------
function IvAccountNotifications() {
  const { state, api } = useStore();
  const profile = state.profile || {};
  const optIn = profile.email_opt_in !== false;
  const segment = state.role === "scout" ? "scout" : "dev";

  const toggle = async (next) => {
    await api.updateProfile({ email_opt_in: next });
  };

  return (
    <div>
      <h2 style={{ marginTop: 0 }}>Email preferences</h2>
      <div className="f-row" style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 16 }}>
        <div>
          <div style={{ fontWeight: 700 }}>Product &amp; feature emails</div>
          <div className="login-note" style={{ margin: 0 }}>Occasional updates about IndieVertical. Account and security emails are always sent.</div>
        </div>
        <IvToggle on={optIn} onChange={toggle} />
      </div>

      <hr style={{ border: "none", borderTop: "1px solid #ddd", margin: "28px 0" }} />
      <h2>New-game &amp; industry newsletter</h2>
      <IvNewsletter
        variant="inline"
        segment={segment}
        source="account-settings"
        force
        heading="The indie games briefing"
        sub="A short, regular read on the indie game industry: trends, standout new projects, and resources worth your time. No spam."
        button="Subscribe"
      />
    </div>
  );
}

// ---------- danger zone tab ----------
function IvAccountDanger() {
  const { api } = useStore();

  const [confirmText, setConfirmText] = React.useState("");
  const [deleting, setDeleting] = React.useState(false);
  const [delErr, setDelErr] = React.useState("");

  const doDelete = async () => {
    setDelErr("");
    setDeleting(true);
    try {
      await api.deleteAccount();
    } catch (err) {
      setDelErr(err && err.message ? err.message : "Could not delete the account. Please contact support.");
      setDeleting(false);
    }
  };

  return (
    <div>
      <h2 style={{ marginTop: 0, color: "#c0392b" }}>Delete account</h2>
      <p className="login-note">
        This permanently deletes your account, your game pages, conversations, and saved games. This can't be undone.
      </p>
      <div className="f-row">
        <label className="f-label" htmlFor="ac-del">Type <strong>DELETE</strong> to confirm</label>
        <input id="ac-del" className="f-input" type="text" value={confirmText} placeholder="DELETE" onChange={(e) => setConfirmText(e.target.value)} />
      </div>
      {delErr && <p style={{ color: "#c0392b", fontSize: 13, margin: "0 0 8px" }}>{delErr}</p>}
      <button
        type="button"
        className="btn btn-primary"
        style={{ background: "#c0392b", borderColor: "#c0392b" }}
        disabled={confirmText !== "DELETE" || deleting}
        onClick={doDelete}
      >
        {deleting ? "Deleting…" : "Delete my account"}
      </button>
    </div>
  );
}

// ---------- page shell ----------
function IvAccount({ tab }) {
  const active = IV_ACCOUNT_TABS.some((t) => t.key === tab) ? tab : "profile";

  let panel = null;
  if (active === "profile") panel = <IvAccountProfile />;
  else if (active === "security") panel = <IvAccountSecurity />;
  else if (active === "notifications") panel = <IvAccountNotifications />;
  else if (active === "danger") panel = <IvAccountDanger />;

  return (
    <div className="pp dir-marquee">
      <IvHeader />
      <div className="su-stage" style={{ flex: 1, alignItems: "flex-start" }}>
        <div className="su-card" style={{ maxWidth: 640, width: "100%" }}>
          <h1 style={{ marginBottom: 16 }}>Account settings</h1>
          <IvAccountNav tab={active} />
          {panel}
        </div>
      </div>
      <IvFooter noNewsletter />
    </div>
  );
}

Object.assign(window, {
  IvAccount, IvAccountProfile, IvAccountSecurity, IvAccountNotifications, IvAccountDanger,
});
