import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
import { Flipper, Flipped } from "react-flip-toolkit";

import { RiMusic2Line, RiUser3Line, RiAlbumLine } from "@remixicon/react";

import type NodeCG from "@nodecg/types";
import type {
  CoverArtReplicant,
  MusicConfigReplicant,
  MusicReplicant,
} from "../types";

const ensureArrayBufferLike = (data: any): ArrayBufferLike => {
  if (data instanceof Uint8Array || data instanceof ArrayBuffer) {
    return data.slice();
  } else if (Array.isArray(data)) {
    return new Uint8Array(data);
  } else if (typeof data === "object" && data !== null) {
    return new Uint8Array(Object.values(data));
  }
  throw new Error("Unsupported data format");
};

const musicReplicant = nodecg.Replicant(
  "musicReplicant",
) as unknown as NodeCG.ServerReplicant<MusicReplicant>;

const musicConfigReplicant = nodecg.Replicant(
  "musicConfigReplicant",
) as unknown as NodeCG.ServerReplicant<MusicConfigReplicant>;

const coverArtReplicant = nodecg.Replicant(
  "coverArtReplicant",
) as unknown as NodeCG.ServerReplicant<CoverArtReplicant>;
const useReplicant = <T = any,>(
  replicant: NodeCG.ServerReplicant<T>,
  fn: (newValue: T | undefined, oldValue: T | undefined) => void | (() => void),
  deps: any[] = [],
) => {
  useEffect(() => {
    let cleanup: (() => void) | void;

    const handler = (newValue: T | undefined, oldValue: T | undefined) => {
      if (cleanup) cleanup(); // Run previous cleanup before setting new one
      cleanup = fn(newValue, oldValue); // Store new cleanup function (if provided)
    };

    replicant.on("change", handler);

    return () => {
      replicant.off("change", handler);
      if (cleanup) cleanup();
    };
  }, [...deps]);
};

export function Index() {
  const [isPlaying, setIsPlaying] = useState(false);
  const [songInfo, setSongInfo] = useState(
    {} as Omit<MusicReplicant, "playing">,
  );

  useReplicant(musicReplicant, (newValue?: MusicReplicant) => {
    setIsPlaying(newValue?.playing || false);
    setSongInfo(newValue || {});
  });

  const [imageUrl, setImageUrl] = useState(null as null | string);

  useReplicant(
    coverArtReplicant,
    (newValue?: CoverArtReplicant) => {
      if (newValue?.data && newValue?.format) {
        const data = ensureArrayBufferLike(newValue.data);

        // Convert to Blob
        const blob = new Blob([data], {
          type: newValue.format,
        });
        const url = URL.createObjectURL(blob);

        setImageUrl(url);

        // Free old image
        return () => {
          URL.revokeObjectURL(url);
        };
      }
    },
    [],
  );

  const [songAlbumToggle, setSongAlbumToggle] = useState(true);
  const [showLabelNames, setShowLabelNames] = useState(true);

  const mainTickerClasses =
    " bg-black text-white line-clamp-3 min-h-[calc(var(--height)_*_1/3)] py-8 text-8xl ";
  const subTickerClasses =
    " min-h-[calc(var(--height)_*_1/5)] bg-[#fdc700] line-clamp-2 py-4 text-5xl ";

  const topTickerClasses = songAlbumToggle
    ? mainTickerClasses
    : subTickerClasses;
  const bottomTickerClasses = !songAlbumToggle
    ? mainTickerClasses
    : subTickerClasses;

  const [tickerSwapInterval, setTickerSwapInterval] = useState(25000);

  useReplicant(
    musicConfigReplicant,
    (newValue) => {
      setTickerSwapInterval(newValue!.tickerSwapInterval * 1000);
    },
    [],
  );

  useEffect(() => {
    let value = songAlbumToggle;

    let timeout1 = undefined as undefined | NodeJS.Timeout;
    let timeout2 = undefined as undefined | NodeJS.Timeout;

    const interval = setInterval(() => {
      value = !value;
      setSongAlbumToggle(value);

      timeout1 = setTimeout(() => {
        setShowLabelNames(true);
        timeout2 = setTimeout(
          () => {
            setShowLabelNames(false);
          },
          Math.min(tickerSwapInterval * 0.5, 10000),
        );
      }, tickerSwapInterval / 5);
    }, tickerSwapInterval);

    return () => {
      setSongAlbumToggle(true);
      setShowLabelNames(false);

      clearInterval(interval);
      clearTimeout(timeout1);
      clearTimeout(timeout2);
    };
  }, [tickerSwapInterval, songInfo]);

  const springOpts = "stiff";

  return (
    <>
      <div
        className="p-16 overflow-hidden drop-shadow-2xl"
        style={{
          display: "flex",
          ...{ "--height": "26vw" },
        }}
      >
        <div className={"flex flex-col relative "}>
          {isPlaying ? (
            <div
              className={
                "flex bg-[#00a63e] text-3xl p-3 text-white relative transition "
              }
              style={{
                transform: `translateY(${songAlbumToggle ? 100 : 0}%)`,
              }}
            >
              <div className="">Godina izdavanja:</div>
              <div className="grow" />
              <div className="font-bold">{songInfo.year}</div>
            </div>
          ) : (
            <></>
          )}

          {imageUrl && isPlaying ? (
            <img
              className={"z-10"}
              src={imageUrl}
              alt="Cover art"
              style={{
                width: "var(--height)",
                height: "var(--height)",
                minWidth: "var(--height)",
                minHeight: "var(--height)",
              }}
            />
          ) : (
            <></>
          )}
        </div>
        {isPlaying ? (
          <div className="block relative flex flex-col">
            <div className="grow" />

            <Flipper
              flipKey={
                songAlbumToggle + "," + showLabelNames + "," + songInfo.name
              }
              spring={springOpts}
            >
              <Flipped flipId="tickers">
                <div
                  className={
                    "inline-flex relative z-30 overflow-visible shrink-0 transition-colors duration-100 flex-col justify-center min-w-(--height) px-8 font-bold " +
                      topTickerClasses +
                      "" ||
                    (showLabelNames && !songAlbumToggle ? " pb-12 pt-8 " : "")
                  }
                >
                  <div
                    className={
                      "absolute flex items-center justify-center transition origin-left overflow-hidden min-h-[calc(var(--height)_/_8)] min-w-[calc(var(--height)_/_8)] text-2xl top-[calc(100%_-_var(--height)_/_10.66)] right-[calc(var(--height)_/_-10.66)] font-bold bg-[#00a63e] h-[var(--height)_/_10.66] text-white top-4 z-20 ml-4 max-w-fit "
                    }
                    style={{
                      transform: showLabelNames ? " scaleX(1) " : " scaleX(0) ",
                    }}
                  >
                    {songAlbumToggle ? (
                      <RiMusic2Line color="white" size={"2.5rem"} />
                    ) : (
                      <RiUser3Line color="white" size={"2.5rem"} />
                    )}
                  </div>

                  {songAlbumToggle ? songInfo.name : songInfo.artist}
                </div>
              </Flipped>
            </Flipper>
            <Flipper flipKey={songAlbumToggle} spring={springOpts}>
              <Flipped flipId="tickers">
                <div
                  className={
                    "inline-flex overflow-visible relative shrink-0 transition-colors duration-100 flex-col justify-center shrink-0 min-w-(--height) px-8 font-bold z-20 " +
                    bottomTickerClasses
                  }
                >
                  <div
                    className={
                      "absolute flex items-center justify-center transition origin-left overflow-hidden min-h-[calc(var(--height)_/_8)] min-w-[calc(var(--height)_/_8)] text-2xl top-[calc(100%_-_var(--height)_/_10.66)] right-[calc(var(--height)_/_-10.66)] font-bold bg-[#00a63e] h-[var(--height)_/_10.66] text-white top-4 ml-4 max-w-fit "
                    }
                    style={{
                      transform: showLabelNames ? " scaleX(1) " : " scaleX(0) ",
                    }}
                  >
                    {songAlbumToggle ? (
                      <RiUser3Line color="white" size={"2.5rem"} />
                    ) : (
                      <RiAlbumLine color="white" size={"2.5rem"} />
                    )}
                  </div>

                  {songAlbumToggle ? songInfo.artist : songInfo.album}
                </div>
              </Flipped>
            </Flipper>
          </div>
        ) : (
          <></>
        )}
      </div>
    </>
  );
}
