import https from "node:https";
import { writeFileSync, readFileSync } from "node:fs";
import { compareVersions } from "../src/utils.js";

interface BrowserRelease {
  engine: string;
  engine_version: string;
  status: string;
  release_date: string;
}

interface BrowserData {
  name: string;
  releases: { [version: string]: BrowserRelease };
}

interface DownstreamBrowsersData {
  browsers: { [browserName: string]: BrowserData };
  lastUpdated: string;
}

interface UserAgent {
  ua: string;
  firstSeen: string;
}

interface UserAgentData {
  uas: UserAgent[];
}

const findLatestVersion = (releases: {
  [version: string]: BrowserRelease;
}): [string, BrowserRelease] | undefined => {
  return Object.entries(releases)
    .sort((a, b) => compareVersions(a[0], b[0]))
    .pop();
};

const handleUas = (
  uaObject: UserAgentData,
): [boolean, DownstreamBrowsersData] => {
  let somethingChanged = false;

  const existingData: DownstreamBrowsersData = JSON.parse(
    readFileSync(process.cwd() + "/static/downstream-browsers.json", {
      encoding: "utf8",
    }),
  );

  const browsers = [
    {
      name: "qq_android",
      latestExistingVersion: findLatestVersion(
        existingData.browsers["qq_android"].releases,
      ),
      regex: new RegExp(/chrome|Chrome\/(\d+).*MQQBrowser\/(\d+.\d+)/),
    },
    {
      name: "uc_android",
      latestExistingVersion: findLatestVersion(
        existingData.browsers["uc_android"].releases,
      ),
      regex: new RegExp(/chrome|Chrome\/(\d+).*UCBrowser\/(\d+.\d+)/),
    },
    {
      name: "ya_android",
      latestExistingVersion: findLatestVersion(
        existingData.browsers["ya_android"].releases,
      ),
      regex: new RegExp(
        /android|Android.*chrome|Chrome\/(\d+).*YaBrowser\/(\d+.\d+)/,
      ),
    },
    {
      name: "kai_os",
      latestExistingVersion: findLatestVersion(
        existingData.browsers["kai_os"].releases,
      ),
      regex: new RegExp(/Firefox\/(\d+).0 KAIOS\/(\d.\d)/),
    },
    {
      name: "facebook_android",
      latestExistingVersion: findLatestVersion(
        existingData.browsers["facebook_android"].releases,
      ),
      regex: new RegExp(/[Chrome|chrome]\/(\d+).*FB4A;FBAV\/(\d+)./),
    },
    {
      name: "instagram_android",
      latestExistingVersion: findLatestVersion(
        existingData.browsers["instagram_android"].releases,
      ),
      regex: new RegExp(/[Chrome|chrome]\/(\d+).*Instagram (\d+)./),
    },
  ];

  uaObject.uas.reverse().forEach((ua) => {
    browsers.forEach((browser) => {
      let browserVersionMatch = ua.ua.match(browser.regex);
      if (browserVersionMatch) {
        let browserName = browser.name;
        let browserVersion: string | undefined;
        let chromiumVersion: string | undefined;
        if (browserVersionMatch[0] == "coc_coc_browser") {
          browserVersion = browserVersionMatch[3].toString().trim();
          chromiumVersion = browserVersionMatch[2].toString().trim();
        } else {
          if (browserVersionMatch[1] && browserVersionMatch[2]) {
            browserVersion = browserVersionMatch[2].toString().trim();
            chromiumVersion = browserVersionMatch[1].toString().trim();
          }
        }
        if (browserVersion !== undefined && chromiumVersion !== undefined) {
          if (
            compareVersions(
              browserVersion,
              browser.latestExistingVersion?.[0] ?? "",
            ) === 1 &&
            parseInt(chromiumVersion) >=
              parseInt(
                browser.latestExistingVersion?.[1].engine_version ?? "",
              ) &&
            !Object.keys(existingData.browsers[browserName].releases).includes(
              browserVersion.toString(),
            )
          ) {
            console.log(
              "adding ",
              browserName,
              " version ",
              browserVersion,
              " with Chromium version ",
              chromiumVersion,
              " and release date ",
              ua.firstSeen,
            );
            existingData.browsers[browserName].releases[browserVersion] = {
              engine: "Blink",
              engine_version: chromiumVersion,
              status: "unknown",
              release_date: ua.firstSeen,
            };
            somethingChanged = true;
          }
        }
      }
    });
  });

  return [somethingChanged, existingData];
};

if (process.argv.length === 2) {
  console.error("Expected at least one argument!");
  process.exit(1);
} else {
  const options = {
    hostname: "api.useragents.io",
    port: 443,
    path: "/api/v1/web-platform-baseline",
    method: "GET",
    headers: {
      "x-api-key": `Bearer ${process.argv[2]}`,
      "Content-Type": " application/json",
    },
  };

  let latestUas: UserAgentData;

  https
    .request(options, (res) => {
      let output: Buffer[] = [];
      console.log("status code: ", res.statusCode);
      res.on("data", (d) => {
        output.push(d);
      });

      res.on("end", () => {
        latestUas = JSON.parse(Buffer.concat(output).toString());
        let [willWrite, fileOutput] = handleUas(latestUas);
        Object.keys(fileOutput.browsers).forEach((browserName) => {
          const releases = fileOutput.browsers[browserName].releases;
          const sortedReleases = Object.entries(releases)
            .sort((a, b) => compareVersions(a[0], b[0]))
            .reduce(
              (acc, [version, release]) => {
                acc[version] = release;
                return acc;
              },
              {} as { [version: string]: BrowserRelease },
            );
          fileOutput.browsers[browserName].releases = sortedReleases;
        });
        if (willWrite) {
          fileOutput.lastUpdated = new Date().toISOString();
          writeFileSync(
            process.cwd() + "/static/downstream-browsers.json",
            JSON.stringify(fileOutput, null, 2),
            { flag: "w" },
          );
          writeFileSync(
            process.cwd() + "/src/data/downstream-browsers.json",
            JSON.stringify(fileOutput, null, 2),
            { flag: "w" },
          );
        } else {
          console.log("no updates at this time");
        }
      });
    })
    .end();
}
