import { type } from "arktype";
import * as semver from "semver";
import { defineDropTask } from "..";
import { systemConfig } from "../../config/sys-conf";
import notificationSystem from "../../notifications";

const latestRelease = type({
  url: "string", // api url for specific release
  html_url: "string", // user facing url
  id: "number", // release id
  tag_name: "string", // tag used for release
  name: "string", // release name
  draft: "boolean",
  prerelease: "boolean",
  created_at: "string",
  published_at: "string",
});

export default defineDropTask({
  buildId: () => `check:update:${new Date().toISOString()}`,
  name: "Check for Update",
  acls: ["system:maintenance:read"],
  taskGroup: "check:update",
  async run({ progress, logger }) {
    // TODO: maybe implement some sort of rate limit thing to prevent this from calling github api a bunch in the event of crashloop or whatever?
    // probably will require custom task scheduler for object cleanup anyway, so something to thing about

    if (!systemConfig.shouldCheckForUpdates()) {
      logger.info("Update check is disabled by configuration");
      progress(100);
      return;
    }

    logger.info("Checking for update");

    const currVerStr = systemConfig.getDropVersion();
    const currVer = semver.coerce(currVerStr);
    if (currVer === null) {
      const msg = "Drop provided a invalid semver tag";
      logger.info(msg);
      throw new Error(msg);
    }
    progress(30);

    const response = await fetch(
      "https://api.github.com/repos/Drop-OSS/drop/releases/latest",
    );
    progress(50);

    // if response failed somehow
    if (!response.ok) {
      logger.info(
        {
          status: response.status,
          body: response.body,
        },
        "Failed to check for update ",
      );

      throw new Error(
        `Failed to check for update: ${response.status} ${response.body}`,
      );
    }

    // parse and validate response
    const resJson = await response.json();
    const body = latestRelease(resJson);
    if (body instanceof type.errors) {
      logger.info(body.summary);
      logger.info("GitHub Api response" + JSON.stringify(resJson));
      throw new Error(
        `GitHub Api response did not match expected schema: ${body.summary}`,
      );
    }

    // parse remote version
    const latestVer = semver.coerce(body.tag_name);
    if (latestVer === null) {
      const msg = "Github Api returned invalid semver tag";
      logger.info(msg);
      throw new Error(msg);
    }
    progress(70);

    // TODO: handle prerelease identifiers https://github.com/npm/node-semver#prerelease-identifiers
    // check if is newer version
    if (semver.gt(latestVer, currVer)) {
      logger.info("Update available");
      notificationSystem.systemPush({
        nonce: `drop-update-available-${currVer}-to-${latestVer}`,
        title: `Update available to v${latestVer}`,
        description: `A new version of Drop is available v${latestVer}`,
        actions: [`View|${body.html_url}`],
        acls: ["system:notifications:read"],
      });
    } else {
      logger.info("no update available");
    }

    logger.info("Done");
    progress(100);
  },
});
