import dayjs from "dayjs";
import type { ContainerInfo, ContainerStats } from "dockerode";

import { db, like, or } from "@homarr/db";
import { icons } from "@homarr/db/schema";

import type { ContainerState } from "../../docker/src";
import { DockerSingleton } from "../../docker/src";
import { createCachedWidgetRequestHandler } from "./lib/cached-widget-request-handler";

export const dockerContainersRequestHandler = createCachedWidgetRequestHandler({
  queryKey: "dockerContainersResult",
  widgetKind: "dockerContainers",
  async requestAsync() {
    const containers = await getContainersWithStatsAsync();

    return containers;
  },
  cacheDuration: dayjs.duration(20, "seconds"),
});

const dockerInstances = DockerSingleton.getInstances();

const extractImage = (container: ContainerInfo) => container.Image.split("/").at(-1)?.split(":").at(0) ?? "";

async function getContainersWithStatsAsync() {
  const containers = await Promise.all(
    dockerInstances.map(async ({ instance, host }) => {
      const instanceContainers = await instance.listContainers({ all: true });
      return instanceContainers.map((container) => ({
        ...container,
        instance: host,
      }));
    }),
  ).then((res) => res.flat());

  const likeQueries = containers.map((container) => like(icons.name, `%${extractImage(container)}%`));

  const dbIcons =
    likeQueries.length > 0
      ? await db.query.icons.findMany({
          where: or(...likeQueries),
        })
      : [];

  const containerStatsPromises = containers.map(async (container) => {
    const instance = dockerInstances.find(({ host }) => host === container.instance)?.instance;
    if (!instance) return null;

    const stats = await instance.getContainer(container.Id).stats({ stream: false });

    return {
      id: container.Id,
      name: container.Names[0]?.split("/")[1] ?? "Unknown",
      state: container.State as ContainerState,
      iconUrl:
        dbIcons.find((icon) => {
          const extractedImage = extractImage(container);
          if (!extractedImage) return false;
          return icon.name.toLowerCase().includes(extractedImage.toLowerCase());
        })?.url ?? null,
      cpuUsage: calculateCpuUsage(stats),
      memoryUsage: stats.memory_stats.usage,
      image: container.Image,
      ports: container.Ports,
    };
  });

  return (await Promise.all(containerStatsPromises)).filter((container) => container !== null);
}

function calculateCpuUsage(stats: ContainerStats): number {
  const cpuDelta = stats.cpu_stats.cpu_usage.total_usage - stats.precpu_stats.cpu_usage.total_usage;
  const systemDelta = stats.cpu_stats.system_cpu_usage - stats.precpu_stats.system_cpu_usage;
  const numberOfCpus = stats.cpu_stats.online_cpus || 1;

  if (systemDelta === 0) return 0;

  return (cpuDelta / systemDelta) * numberOfCpus * 100;
}
