import {useEffect, useRef, useState} from "react";
import {create} from "zustand";

import formatDuration from "../../utils/formatDuration.ts";
import {type LogFilterPredicate, type LogRecord, useLogs} from "./useLogs.ts";
import Filters from "./Filters.tsx";
import {type Cell, useCell} from "../../utils/cellStore.ts";

import styles from "./Logging.module.css";

type LoggingSettings = {
  showRelativeTime: boolean;
  setShowRelativeTime: (showRelativeTime: boolean) => void;
  scrollToBottom: boolean;
  setScrollToBottom: (scrollToBottom: boolean) => void;
};

const useLoggingSettings = create<LoggingSettings>((set) => ({
  showRelativeTime: false,
  setShowRelativeTime: (showRelativeTime: boolean) => set({ showRelativeTime }),
  scrollToBottom: true,
  setScrollToBottom: (scrollToBottom: boolean) => set({ scrollToBottom }),
}));

function LogMessage({
  recordCell,
  onUpdate,
}: {
  recordCell: Cell<LogRecord>,
  onUpdate?: () => void,
}) {
  const { showRelativeTime, setShowRelativeTime } = useLoggingSettings();
  const record = useCell(recordCell);

  /**
   * Normalizes the log level number to a multiple of 10, for which there are CSS styles.
   */
  const normalizedLevelNo = (() => {
    // By default, the highest level is 50 (CRITICAL). Custom levels can be higher, but we don't have more critical color.
    if (record.levelno >= 50) return 50;

    return Math.round(record.levelno / 10) * 10;
  })();

  const normalizedName = record.name.split(".").pop() || record.name;

  useEffect(() => {
    if (onUpdate) onUpdate();
  }, [record, onUpdate]);

  return <div className={`${styles.logContainer} round-md border-lg flex-row gap-md`}>
    <div className={`${styles[`accented${normalizedLevelNo}`]} flex-col padding-sm justify-between`}>
      <span className={"mono bold"}>{record.levelname}</span>
      <span className={"mono clickable font-small"}
        onClick={() => setShowRelativeTime(!showRelativeTime)}
      >{showRelativeTime
        ? formatDuration(record.relativeCreated)
        : new Date(record.created * 1000).toLocaleTimeString()
      }</span>
    </div>
    <div className={"flex-col flex-1 padding-sm"}>
      <span className={"mono"}>{normalizedName}</span>
      <span>{record.message}</span>
    </div>
  </div>;
}

function LogMessages({ recordCells }: { recordCells: Cell<LogRecord>[] }) {
  const scrollableRef = useRef<HTMLDivElement>(null);
  const lastElementRef = useRef<HTMLLIElement>(null)
  const { scrollToBottom, setScrollToBottom } = useLoggingSettings();

  useEffect(() => {
    if (!scrollableRef.current) return;
    const currentScrollableRef = scrollableRef.current;

    const handleScroll = () => setScrollToBottom(false);

    currentScrollableRef.addEventListener("wheel", handleScroll);
    currentScrollableRef.addEventListener("touchmove", handleScroll);

    return () => {
      currentScrollableRef.removeEventListener("wheel", handleScroll);
      currentScrollableRef.removeEventListener("touchmove", handleScroll);
    }
  }, [scrollableRef, setScrollToBottom]);

  function scrollLastElementIntoView(force = false) {
    if ((!scrollToBottom && !force) || !lastElementRef.current) return;
    lastElementRef.current.scrollIntoView({ behavior: "smooth" });
  }

  return <div ref={scrollableRef} className={"min-height-0 scroll-y padding-b-md"}>
    <ol className={`${styles.noNumbers} margin-0 flex-col gap-md`}>
      {recordCells.map((recordCell, i) => (
        <li key={`${i}_${recordCell.get().firstRelativeCreated}`}>
          <LogMessage recordCell={recordCell} onUpdate={scrollLastElementIntoView} />
        </li>
      ))}
      <li ref={lastElementRef}></li>
    </ol>
    {!scrollToBottom && <button
      className={styles.floatingButton}
      onClick={() => {
        setScrollToBottom(true);
        scrollLastElementIntoView(true);
      }}
    >
      Scroll to bottom
    </button>}
  </div>;
}

export default function Logging() {
  const [filterPredicates, setFilterPredicates] = useState(new Map<string, LogFilterPredicate>());
  const { filteredLogs, distinctNames } = useLogs(filterPredicates)

  return <div className={`flex-col gap-lg min-height-0 ${styles.loggingContainer}`}>
    <div className={"flex-row gap-lg justify-between align-center"}>
      <h2 className={"margin-0"}>Logs</h2>
      <Filters
        filterPredicates={filterPredicates}
        setFilterPredicates={setFilterPredicates}
        agentNames={distinctNames}
      />
    </div>
    <LogMessages recordCells={filteredLogs} />
  </div>;
}
