/* eslint-disable react-hooks/exhaustive-deps */
import { useState, useRef, useEffect } from "react";
import ModalWrapper from "../ModalWrapper";
import Button from "../ui/Button";
import Select from "../ui/Select";
import RestartIcon from "../icons/RestartIcon";
import useModalStore from "../../store/modalStore";
import SettingsModal from "./SettingsModal";
import clsx from "clsx";
import { isMediaDevicesSupported } from "../../lib/mediaDevices";

interface VoiceCheckModalProps {
  selectedMicrophone: string;
  microphones: { deviceId: string; label: string }[];
  microphoneVolume: number;
  onSelectMicrophone: (label: string) => void;
}

function VoiceCheckModal({
  selectedMicrophone,
  microphones,
  microphoneVolume,
  onSelectMicrophone,
}: VoiceCheckModalProps) {
  const { setModal } = useModalStore();

  const [audioLevel, setAudioLevel] = useState(0);
  const [status, setStatus] = useState<"default" | "success" | "error">(
    "default"
  );
  const [isTestRunning, setIsTestRunning] = useState(false);
  const [soundDetected, setSoundDetected] = useState(false);
  const [maxAudioLevel, setMaxAudioLevel] = useState(0);

  const audioContextRef = useRef<AudioContext | null>(null);
  const analyserRef = useRef<AnalyserNode | null>(null);
  const streamRef = useRef<MediaStream | null>(null);
  const sourceRef = useRef<MediaStreamAudioSourceNode | null>(null);
  const gainNodeRef = useRef<GainNode | null>(null);
  const animationFrameRef = useRef<number | null>(null);
  const testTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const statusRef = useRef<"default" | "success" | "error">("default");

  function detectAudioLevel() {
    if (!analyserRef.current) return;

    const dataArray = new Uint8Array(analyserRef.current.frequencyBinCount);
    analyserRef.current.getByteFrequencyData(dataArray);

    const average = dataArray.reduce((a, b) => a + b) / dataArray.length;
    setAudioLevel(average);

    // Обновляем максимальный уровень звука
    setMaxAudioLevel((prev) => Math.max(prev, average));

    // Если звук обнаружен и статус ещё не success, устанавливаем success
    if (average > 5) {
      if (statusRef.current !== "success") {
        statusRef.current = "success";
        setStatus("success");
      }
      setSoundDetected(true);
    }

    animationFrameRef.current = requestAnimationFrame(detectAudioLevel);
  }

  async function startMicrophoneTest() {
    // Проверяем доступность API
    if (!isMediaDevicesSupported()) {
      console.error(
        "navigator.mediaDevices недоступен. Возможно, требуется HTTPS или поддержка браузера."
      );
      setStatus("error");
      setIsTestRunning(false);
      return;
    }

    try {
      const selectedMic = microphones.find(
        (mic) => mic.label === selectedMicrophone
      );

      const constraints: MediaStreamConstraints = {
        audio: selectedMic
          ? { deviceId: { exact: selectedMic.deviceId } }
          : true,
      };

      const stream = await navigator.mediaDevices.getUserMedia(constraints);
      streamRef.current = stream;

      const audioContext = new AudioContext();
      audioContextRef.current = audioContext;

      const analyser = audioContext.createAnalyser();
      analyser.fftSize = 256;
      analyserRef.current = analyser;

      const gainNode = audioContext.createGain();
      gainNode.gain.value = microphoneVolume / 100;
      gainNodeRef.current = gainNode;

      const source = audioContext.createMediaStreamSource(stream);
      sourceRef.current = source;

      // Подключаем: source -> gainNode -> analyser -> destination
      source.connect(gainNode);
      gainNode.connect(analyser);
      analyser.connect(audioContext.destination);

      // Сбрасываем статус при новом тесте
      statusRef.current = "default";
      setStatus("default");
      setSoundDetected(false);
      setAudioLevel(0);
      setMaxAudioLevel(0);
      setIsTestRunning(true);

      // Запускаем проверку звука
      detectAudioLevel();

      // Останавливаем проверку через 3 секунды и устанавливаем результат
      testTimeoutRef.current = setTimeout(() => {
        // Останавливаем анимацию
        if (animationFrameRef.current) {
          cancelAnimationFrame(animationFrameRef.current);
          animationFrameRef.current = null;
        }

        // Устанавливаем финальный статус
        if (statusRef.current === "default") {
          statusRef.current = "error";
          setStatus("error");
        }
        // Если статус уже success, он остаётся success

        setIsTestRunning(false);

        // Очищаем ресурсы после завершения теста
        cleanupAudioResources();
      }, 3000);
    } catch (error) {
      console.error("Ошибка доступа к микрофону:", error);
      setStatus("error");
      setIsTestRunning(false);
    }
  }

  function cleanupAudioResources() {
    // Отключаем source
    if (sourceRef.current) {
      try {
        sourceRef.current.disconnect();
      } catch {
        // Ignore if already disconnected
      }
      sourceRef.current = null;
    }

    // Отключаем gainNode
    if (gainNodeRef.current) {
      try {
        gainNodeRef.current.disconnect();
      } catch {
        // Ignore if already disconnected
      }
      gainNodeRef.current = null;
    }

    // Отключаем analyser
    if (analyserRef.current) {
      try {
        analyserRef.current.disconnect();
      } catch {
        // Ignore if already disconnected
      }
      analyserRef.current = null;
    }

    // Останавливаем все треки медиа-потока
    if (streamRef.current) {
      streamRef.current.getTracks().forEach((track) => track.stop());
      streamRef.current = null;
    }

    // Закрываем AudioContext
    if (audioContextRef.current) {
      audioContextRef.current.close();
      audioContextRef.current = null;
    }
  }

  function stopMicrophoneTest() {
    if (animationFrameRef.current) {
      cancelAnimationFrame(animationFrameRef.current);
      animationFrameRef.current = null;
    }
    if (testTimeoutRef.current) {
      clearTimeout(testTimeoutRef.current);
      testTimeoutRef.current = null;
    }

    cleanupAudioResources();

    // НЕ сбрасываем audioLevel и maxAudioLevel - они сохраняются до нового теста
    setSoundDetected(false);
    setIsTestRunning(false);
  }

  function restartMicrophoneTest() {
    stopMicrophoneTest();
    // Небольшая задержка перед запуском нового теста
    setTimeout(startMicrophoneTest, 100);
  }

  useEffect(() => {
    startMicrophoneTest();

    return stopMicrophoneTest;
  }, [selectedMicrophone]);

  // Обновляем громкость микрофона при изменении слайдера
  useEffect(() => {
    if (gainNodeRef.current) {
      gainNodeRef.current.gain.value = microphoneVolume / 100;
    }
  }, [microphoneVolume]);

  // Генерируем высоты для баров на основе уровня звука
  function generateBarHeights() {
    const baseHeights = [
      3, 12, 16, 20, 44, 28, 34, 60, 8, 40, 18, 36, 24, 13, 10, 6,
    ];
    // Используем maxAudioLevel для сохранения максимальной высоты
    const levelToUse = isTestRunning ? audioLevel : maxAudioLevel;
    const multiplier = Math.min(levelToUse / 5);
    return baseHeights.map((h) => Math.max(3, Math.min(60, h * multiplier)));
  }

  const barHeights = generateBarHeights();

  console.log(barHeights);

  return (
    <ModalWrapper className="2xl:max-w-[21.111vw] max-w-[304px]">
      <div className="flex flex-col 2xl:gap-[1.667vw] gap-6">
        {/* Выбор микрофона */}
        <div className="flex flex-col items-center 2xl:gap-[0.278vw] gap-1">
          <p className="caption-xs text-[#7D7D7D] font-medium">Микрофон</p>
          <Select
            options={microphones.map((m) => m.label)}
            defaultOption={selectedMicrophone}
            onSelect={onSelectMicrophone}
          />
        </div>

        {/* Визуализация уровня звука */}
        <div className="flex flex-col items-center 2xl:gap-[0.556vw] gap-2">
          <div className="flex items-center justify-center 2xl:gap-[0.208vw] gap-[3px] 2xl:h-[4.167vw] h-[60px]">
            {barHeights.map((height, index) => (
              <div
                key={index}
                className={clsx(
                  `2xl:rounded-[0.556vw] rounded-lg transition-all duration-150 2xl:w-[0.208vw] w-[3px]`,
                  soundDetected ? "bg-[#7B60F3]" : "bg-[#D6D6D6]"
                )}
                style={{ height: `${(height / 1440) * innerWidth}px` }}
              />
            ))}
          </div>
          {!isTestRunning && status === "success" && (
            <p className="caption-s text-[#29AF61] font-medium">Звук есть!</p>
          )}
          {!isTestRunning && status === "error" && (
            <p className="caption-s text-[#FF4517] font-medium">
              Звук не обнаружен
            </p>
          )}
          {isTestRunning && (
            <p className="caption-s text-[#7D7D7D] font-medium">Проверка...</p>
          )}
        </div>

        <div className="flex flex-col items-center 2xl:gap-[0.556vw] gap-2 text-center">
          <h2 className="title-l font-medium">Говорите</h2>
          <p className="text-s text-[#7D7D7D]">
            Вы должны видеть движение на&nbsp;индикаторе
          </p>
        </div>

        <div className="flex flex-col 2xl:gap-[0.556vw] gap-2">
          <Button
            variant="secondary"
            size="large"
            onClick={restartMicrophoneTest}
            disabled={isTestRunning}
            className="w-full"
          >
            <div className="2xl:size-[1.389vw] size-5">
              <RestartIcon />
            </div>
            Повторить проверку
          </Button>

          <Button
            variant="primary"
            size="large"
            onClick={() => setModal(<SettingsModal />)}
            className="w-full"
          >
            Завершить
          </Button>
        </div>
      </div>
    </ModalWrapper>
  );
}

export default VoiceCheckModal;
