/* GitHub Releases helper.
 *
 * Set REPO to "owner/repo" (e.g. "janedoe/my-crafty-stash") and the buttons
 * on the Download page will fetch the latest official release and grab the
 * first asset whose filename matches the supplied pattern.
 *
 * Patterns are simple wildcards: "*Setup*.exe", "*Portable*.zip", etc.
 *
 * If REPO is left as the placeholder OR the network call fails, the buttons
 * fall back to the simulated download dialog (the old behavior) so the demo
 * still works.
 */

const RELEASE_CONFIG = /*EDITMODE-BEGIN*/{
  "repo": "TequilaJosh/MyCraftyStash",
  "installerPattern": "*Setup*.exe",
  "portablePattern": "*Portable*.zip"
}/*EDITMODE-END*/;

// Cache the latest-release lookup for the page's lifetime so two buttons
// don't fire two API calls.
let _releasePromise = null;
function fetchLatestRelease() {
  if (_releasePromise) return _releasePromise;
  const { repo } = RELEASE_CONFIG;
  if (!repo || repo === "OWNER/REPO") {
    _releasePromise = Promise.resolve(null);
    return _releasePromise;
  }
  _releasePromise = fetch(`https://api.github.com/repos/${repo}/releases/latest`, {
    headers: { Accept: "application/vnd.github+json" },
  })
    .then((r) => (r.ok ? r.json() : null))
    .then((latest) => {
      // /releases/latest excludes pre-releases and drafts. If there's no
      // stable "latest" release yet (e.g. the repo only has a beta/pre-release),
      // fall back to the most recent release from the full list.
      if (latest) return latest;
      return fetch(`https://api.github.com/repos/${repo}/releases`, {
        headers: { Accept: "application/vnd.github+json" },
      })
        .then((r) => (r.ok ? r.json() : null))
        .then((list) => (Array.isArray(list) && list.length ? list[0] : null))
        .catch(() => null);
    })
    .catch(() => null);
  return _releasePromise;
}

function patternToRegex(pattern) {
  // Escape regex special chars except '*', then turn '*' into '.*'.
  const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
  return new RegExp(`^${escaped}$`, "i");
}

function pickAsset(release, pattern) {
  if (!release || !Array.isArray(release.assets)) return null;
  const re = patternToRegex(pattern);
  return release.assets.find((a) => re.test(a.name)) || null;
}

/* React hook: returns { url, name, sizeMb, version, loading, error } for a
 * given pattern. url is null until the asset is found. */
function useReleaseAsset(pattern) {
  const [state, setState] = React.useState({
    url: null, name: null, sizeMb: null, version: null,
    loading: true, error: null,
  });
  React.useEffect(() => {
    let cancelled = false;
    fetchLatestRelease().then((release) => {
      if (cancelled) return;
      if (!release) {
        setState({ url: null, name: null, sizeMb: null, version: null, loading: false, error: "no-repo" });
        return;
      }
      const asset = pickAsset(release, pattern);
      if (!asset) {
        setState({
          url: null, name: null, sizeMb: null,
          version: release.tag_name || null,
          loading: false, error: "no-asset",
        });
        return;
      }
      setState({
        url: asset.browser_download_url,
        name: asset.name,
        sizeMb: asset.size ? +(asset.size / (1024 * 1024)).toFixed(1) : null,
        version: release.tag_name || null,
        loading: false,
        error: null,
      });
    });
    return () => { cancelled = true; };
  }, [pattern]);
  return state;
}

/* Parse a GitHub release body into changelog bullets.
 * Bullets start with "- ", "* " or "•"; indented continuation lines are
 * merged into the bullet above (the publish script's notes wrap long
 * bullets across lines). Markdown headers and blanks are skipped.
 * Each bullet gets a display tag: "fix" when it reads like a fix,
 * otherwise "new". */
function parseReleaseBullets(body) {
  if (!body) return [];
  const bullets = [];
  for (const raw of body.split("\n")) {
    const line = raw.trim();
    if (!line || line.startsWith("#")) continue;
    let text = null;
    if (line.startsWith("- ")) text = line.slice(2).trim();
    else if (line.startsWith("* ")) text = line.slice(2).trim();
    else if (line.startsWith("•")) text = line.replace(/^•+\s*/, "");
    if (text !== null) {
      bullets.push(text);
    } else if (bullets.length > 0) {
      bullets[bullets.length - 1] += " " + line; // continuation line
    }
  }
  return bullets.map((t) => ({
    tag: /^fix(es|ed)?\b/i.test(t) ? "fix" : "new",
    text: t,
  }));
}

/* React hook: latest release's version + parsed changelog bullets for the
 * "What's new" section on the Download page. */
function useReleaseChangelog() {
  const [state, setState] = React.useState({ version: null, bullets: [], loading: true });
  React.useEffect(() => {
    let cancelled = false;
    fetchLatestRelease().then((release) => {
      if (cancelled) return;
      setState({
        version: release?.tag_name || null,
        bullets: parseReleaseBullets(release?.body),
        loading: false,
      });
    });
    return () => { cancelled = true; };
  }, []);
  return state;
}

/* Trigger an actual browser download for a URL. Uses an <a download> click
 * so the file is saved instead of navigated to. */
function triggerDownload(url, suggestedName) {
  const a = document.createElement("a");
  a.href = url;
  if (suggestedName) a.download = suggestedName;
  a.rel = "noopener";
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}

Object.assign(window, {
  RELEASE_CONFIG,
  fetchLatestRelease,
  useReleaseAsset,
  useReleaseChangelog,
  triggerDownload,
});
