// once a minute (what's the point if it's out of date!)
// do days since last update (0 will be obvious then), should (please please please let this be true) only need three digits MAX
// until hs2 ends of course but let's not think about that now
// okay aaaa hs2 will end that will be sad and good i guess but
// not important right now

// would be neat (but painful) to have it actually roll like an analog thing

import { contentType } from "https://deno.land/std@0.202.0/media_types/mod.ts";
import {
  DOMParser,
} from "https://deno.land/x/deno_dom@v0.1.38/deno-dom-wasm.ts";
import {
  Image,
  TextLayout,
} from "https://deno.land/x/imagescript@1.2.15/ImageScript.js";

// not sure how worthwhile this optimization is but
const dom_parser = new DOMParser();

async function get_last_update_date(): Promise<
  { last_updated: Date; last_update_count: number } | string
> {
  try {
    const res = await fetch("https://homestuck2.com/story/log");
    const body = await res.text();
    if (!body) return "couldn't get a string body";

    // could just regex for the date lol
    const doc = dom_parser.parseFromString(body, "text/html");
    if (!doc) return "couldn't parse the body into the DOM";

    const time = doc.querySelector("time");
    if (!time) return "couldn't get even a single time from the doc (bad!)";
    console.log(time.attributes.getNamedItem("datetime")?.value);

    const datetime = time.attributes.getNamedItem("datetime")?.value;
    if (!datetime) return "time didn't have datetime attr??";
    const date = new Date(datetime);
    // should work until the year 10000
    // includes the hour for safety (NVM: WHAT IF IT ROLLED OVER (ugh let's hope that doesn't happen for the day))
    const date_prefix = date.toISOString().slice(0, 10);
    return {
      last_updated: date,
      last_update_count:
        doc.querySelectorAll(`time[datetime^="${date_prefix}"]`)
          .length,
    };
  } catch (e) {
    return `caught error: ${e}`;
  }
}

async function check_again() {
  last_checked = new Date();

  const stuff = await get_last_update_date();
  if (typeof stuff == "string") {
    console.log(`failed to get a date when checking 😬: '${stuff}'`);
    console.log(
      "^ ironically enough, this is more likely when there *is* an update",
    );
    console.log(
      "^ unironically, this is *most* likely the result of a bug",
    );
    return;
  }
  last_updated = stuff.last_updated;
  last_update_count = stuff.last_update_count;
}

function days_since_update(): number {
  const millis = new Date().getTime() - last_updated.getTime();
  return millis / (24 * 60 * 60 * 1000);
}

// silly, i know
let last_updated: Date = new Date(0);
let last_checked: Date = new Date();
let last_update_count = 0;
await check_again();

// *definitely* a worthwhile optimization
// (still inefficient to go over http...)
const font = await fetch("https://static.pyrope.net/courier-std-bold.otf")
  .then((r) => r.arrayBuffer())
  .then((b) => new Uint8Array(b));

function make_image(n: number): Image {
  const days = Math.floor(n).toString().padStart(3, "0");
  let image = new Image(110, 44);

  const outline = Image.renderText(
    font,
    64,
    days,
    0xff_ff_ff_ff,
    new TextLayout({ verticalAlign: "center", horizontalAlign: "middle" }),
  );

  for (const hshift of [-2, 0, 2]) {
    for (const vshift of [-2, 0, 2]) {
      image = image.composite(outline, hshift - 4, vshift);
    }
  }
  const text = Image.renderText(
    font,
    64,
    days,
    0x00_00_00_ff,
    new TextLayout({ verticalAlign: "center", horizontalAlign: "middle" }),
  );

  return image.composite(text, -4);
}

async function update_images() {
  [last_updated_image, last_update_count_image] = await Promise.all([
    make_image(days_since_update()).encode(),
    make_image(last_update_count).encode(),
  ]);
}

let last_updated_image: Uint8Array = new Uint8Array();
let last_update_count_image: Uint8Array = new Uint8Array();
await update_images();

const image_response = (data: Uint8Array): Response =>
  new Response(data, {
    headers: { "Content-Type": contentType("png") },
  });

Deno.serve({ port: 61264 }, async (req) => {
  const url = new URL(req.url);
  if ((new Date().getTime() - last_checked.getTime()) >= 60 * 1000) {
    await check_again();
    await update_images();
  }

  // could use URLPattern but that would be overkill for this
  if (url.pathname.match("count")) {
    return image_response(last_update_count_image);
  } else {
    return image_response(last_updated_image);
  }
});
