import { hex_color_by_intention } from "@lib/color";
import { useRead } from "@lib/hooks";
import { Types } from "komodo_client";
import { useMemo } from "react";
import { useStatsGranularity } from "./hooks";
import { Loader2, OctagonAlert } from "lucide-react";
import { AxisOptions, Chart } from "react-charts";
import { convertTsMsToLocalUnixTsInMs } from "@lib/utils";
import { useTheme } from "@ui/theme";
import { fmt_utc_date } from "@lib/formatting";

type StatType =
  | "Cpu"
  | "Memory"
  | "Disk"
  | "Network Ingress"
  | "Network Egress"
  | "Load Average";

type StatDatapoint = { date: number; value: number };

export const StatChart = ({
  server_id,
  type,
  className,
}: {
  server_id: string;
  type: StatType;
  className?: string;
}) => {
  const [granularity] = useStatsGranularity();

  const { data, isPending } = useRead(
    "GetHistoricalServerStats",
    {
      server: server_id,
      granularity,
    },
    {
      refetchInterval:
        granularity === Types.Timelength.FiveSeconds
          ? 5_000
          : granularity === Types.Timelength.FifteenSeconds
            ? 10_000
            : 15_000,
    }
  );

  const seriesData = useMemo(() => {
    if (!data?.stats) return [] as { label: string; data: StatDatapoint[] }[];
    const records = [...data.stats].reverse();
    if (type === "Load Average") {
      const one = records.map((s) => ({
        date: convertTsMsToLocalUnixTsInMs(s.ts),
        value: s.load_average?.one ?? 0,
      }));
      const five = records.map((s) => ({
        date: convertTsMsToLocalUnixTsInMs(s.ts),
        value: s.load_average?.five ?? 0,
      }));
      const fifteen = records.map((s) => ({
        date: convertTsMsToLocalUnixTsInMs(s.ts),
        value: s.load_average?.fifteen ?? 0,
      }));
      return [
        { label: "1m", data: one },
        { label: "5m", data: five },
        { label: "15m", data: fifteen },
      ];
    }
    const single = records.map((stat) => ({
      date: convertTsMsToLocalUnixTsInMs(stat.ts),
      value: getStat(stat, type),
    }));
    return [{ label: type, data: single }];
  }, [data, type]);

  return (
    <div className={className}>
      <h1 className="px-2 py-1">{type}</h1>
      {isPending ? (
        <div className="w-full max-w-full h-full flex items-center justify-center">
          <Loader2 className="w-8 h-8 animate-spin" />
        </div>
      ) : (
        <InnerStatChart
          type={type}
          stats={seriesData.flatMap((s) => s.data)}
          seriesData={seriesData}
        />
      )}
    </div>
  );
};

const BYTES_PER_GB = 1073741824.0;
const BYTES_PER_MB = 1048576.0;
const BYTES_PER_KB = 1024.0;

export const InnerStatChart = ({
  type,
  stats,
  seriesData,
}: {
  type: StatType;
  stats: StatDatapoint[] | undefined;
  seriesData?: { label: string; data: StatDatapoint[] }[];
}) => {
  const { currentTheme } = useTheme();

  const min = stats?.[0]?.date ?? 0;
  const max = stats?.[stats.length - 1]?.date ?? 0;
  const diff = max - min;

  const timeAxis = useMemo((): AxisOptions<StatDatapoint> => {
    return {
      getValue: (datum) => new Date(datum.date),
      hardMax: new Date(max + diff * 0.02),
      hardMin: new Date(min - diff * 0.02),
      tickCount: 6,
      formatters: {
        // scale: (value?: Date) => fmt_date(value ?? new Date()),
        tooltip: (value?: Date) => (
          <div className="text-lg font-mono">
            {fmt_utc_date(value ?? new Date())}
          </div>
        ),
        cursor: (_value?: Date) => false,
      },
    };
  }, [min, max, diff]);

  const maxStatValue = stats ? Math.max(...stats.map((s) => s.value)) : 0;

  const { maxUnitValue, unitValue, unit } = useMemo(() => {
    if (type === "Network Ingress" || type === "Network Egress") {
      const maxUnitValue = 2 ** Math.ceil(Math.log2(maxStatValue));
      if (maxStatValue <= BYTES_PER_MB) {
        return {
          unit: "KB",
          unitValue: BYTES_PER_KB,
          maxUnitValue,
        };
      } else if (maxStatValue <= BYTES_PER_GB) {
        return {
          unit: "MB",
          unitValue: BYTES_PER_MB,
          maxUnitValue,
        };
      } else {
        return {
          unit: "GB",
          unitValue: BYTES_PER_GB,
          maxUnitValue,
        };
      }
    }
    if (type === "Load Average") {
      return {
        maxUnitValue: maxStatValue === 0 ? 1 : maxStatValue * 1.2,
      };
    }
    if (type === "Cpu") {
      return {
        maxUnitValue: Math.min(2 ** (Math.log2(maxStatValue) + 0.3), 100),
      };
    }
    return { maxUnitValue: 100 }; // Default for memory, disk
  }, [type, maxStatValue]);

  const valueAxis = useMemo(
    (): AxisOptions<StatDatapoint>[] => [
      {
        getValue: (datum) => datum.value,
        elementType: type === "Load Average" ? "line" : "area",
        stacked: type !== "Load Average",
        min: 0,
        max: maxUnitValue,
        formatters: {
          tooltip: (value?: number) => (
            <div className="text-lg font-mono">
              {(type === "Network Ingress" || type === "Network Egress") && unit
                ? `${((value ?? 0) / unitValue).toFixed(2)} ${unit}`
                : type === "Load Average"
                  ? `${(value ?? 0).toFixed(2)}`
                  : `${value?.toFixed(2)}%`}
            </div>
          ),
        },
      },
    ],
    [type, maxUnitValue, unit]
  );

  if ((seriesData?.[0]?.data.length ?? 0) < 2) {
    return (
      <div className="w-full h-full flex gap-4 justify-center items-center">
        <OctagonAlert className="w-6 h-6" />
        <h1>Not enough data yet, choose a smaller interval.</h1>
      </div>
    );
  }

  return (
    <Chart
      options={{
        data: seriesData ?? [{ label: type, data: stats ?? [] }],
        primaryAxis: timeAxis,
        secondaryAxes: valueAxis,
        defaultColors:
          type === "Load Average"
            ? [
                hex_color_by_intention("Good"),
                hex_color_by_intention("Neutral"),
                hex_color_by_intention("Unknown"),
              ]
            : [getColor(type)],
        dark: currentTheme === "dark",
        padding: {
          left: 10,
          right: 10,
        },
        // tooltip: {
        //   showDatumInTooltip: () => false,
        // },
      }}
    />
  );
};

const getStat = (stat: Types.SystemStatsRecord, type: StatType) => {
  if (type === "Cpu") return stat.cpu_perc || 0;
  if (type === "Memory") return (100 * stat.mem_used_gb) / stat.mem_total_gb;
  if (type === "Disk") return (100 * stat.disk_used_gb) / stat.disk_total_gb;
  if (type === "Network Ingress") return stat.network_ingress_bytes || 0;
  if (type === "Network Egress") return stat.network_egress_bytes || 0;
  return 0;
};

const getColor = (type: StatType) => {
  if (type === "Cpu") return hex_color_by_intention("Good");
  if (type === "Memory") return hex_color_by_intention("Warning");
  if (type === "Disk") return hex_color_by_intention("Neutral");
  if (type === "Network Ingress") return hex_color_by_intention("Good");
  if (type === "Network Egress") return hex_color_by_intention("Critical");
  return hex_color_by_intention("Unknown");
};
