/* 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 useModalStore from "../../store/modalStore";
import SettingsModal from "./SettingsModal";
import clsx from "clsx";
import { isMediaDevicesSupported } from "../../lib/mediaDevices";

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

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

  const [selectedMicrophone, setSelectedMicrophone] =
    useState(initialMicrophone);
  const [isActive, setIsActive] = useState(false);
  const [currentVolume, setCurrentVolume] = useState(0);
  const [detectionStatus, setDetectionStatus] = useState<
    "waiting" | "detected" | "not_detected"
  >("waiting");

  // Обработчик выбора микрофона
  const handleSelectMicrophone = (label: string) => {
    setSelectedMicrophone(label);
    onSelectMicrophone(label);
  };

  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 detectionTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
    null
  );
  const soundDetectedRef = useRef(false);

  async function startMicrophoneMonitoring() {
    // Проверяем доступность API
    if (!isMediaDevicesSupported()) {
      console.error(
        "navigator.mediaDevices недоступен. Возможно, требуется HTTPS или поддержка браузера."
      );
      setIsActive(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 = 2048;
      analyser.smoothingTimeConstant = 0.85;
      analyser.minDecibels = -90;
      analyser.maxDecibels = -10;
      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);

      // Сбрасываем состояние обнаружения
      soundDetectedRef.current = false;
      setDetectionStatus("waiting");
      setIsActive(true);

      // Через 3 секунды проверяем, был ли обнаружен звук
      detectionTimeoutRef.current = setTimeout(() => {
        if (soundDetectedRef.current) {
          setDetectionStatus("detected");
        } else {
          setDetectionStatus("not_detected");
        }
      }, 3000);
    } catch (error) {
      console.error("Ошибка доступа к микрофону:", error);
      setIsActive(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 stopMicrophoneMonitoring() {
    if (animationFrameRef.current) {
      cancelAnimationFrame(animationFrameRef.current);
      animationFrameRef.current = null;
    }

    if (detectionTimeoutRef.current) {
      clearTimeout(detectionTimeoutRef.current);
      detectionTimeoutRef.current = null;
    }

    cleanupAudioResources();

    setCurrentVolume(0);
    setIsActive(false);
    soundDetectedRef.current = false;
  }

  useEffect(() => {
    startMicrophoneMonitoring();

    return stopMicrophoneMonitoring;
  }, [selectedMicrophone]);

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

  // Состояние для баров эквалайзера (40 баров для плавных волн)
  const barCount = 40;
  const [barHeights, setBarHeights] = useState<number[]>(
    new Array(barCount).fill(3)
  );

  // Обновляем бары эквалайзера в режиме реального времени
  useEffect(() => {
    if (!isActive || !analyserRef.current) return;

    const updateBars = () => {
      if (!analyserRef.current) return;

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

      // Сначала создаём меньше опорных точек для интерполяции
      const keyPoints = Math.floor(barCount / 2.5); // ~16 опорных точек
      const keyHeights: number[] = [];

      for (let i = 0; i < keyPoints; i++) {
        // Логарифмическое распределение частот
        const startFreq = Math.pow(2, (i / keyPoints) * 10);
        const endFreq = Math.pow(2, ((i + 1) / keyPoints) * 10);

        const startIndex = Math.floor((startFreq / 1024) * bufferLength);
        const endIndex = Math.floor((endFreq / 1024) * bufferLength);

        let sum = 0;
        let count = 0;

        for (let j = startIndex; j < endIndex && j < bufferLength; j++) {
          sum += dataArray[j];
          count++;
        }

        const average = count > 0 ? sum / count : 0;
        // Нормализуем к диапазону 3-60 пикселей
        const normalizedHeight = Math.min(
          60,
          Math.max(3, (average / 255) * 60 * 2.5)
        );
        keyHeights.push(normalizedHeight);
      }

      // Интерполируем между опорными точками для плавных волн
      const newBarHeights: number[] = [];
      for (let i = 0; i < barCount; i++) {
        const position = (i / barCount) * (keyPoints - 1);
        const index = Math.floor(position);
        const fraction = position - index;

        if (index >= keyPoints - 1) {
          newBarHeights.push(keyHeights[keyPoints - 1]);
        } else {
          // Линейная интерполяция между двумя опорными точками
          const interpolated =
            keyHeights[index] * (1 - fraction) +
            keyHeights[index + 1] * fraction;
          newBarHeights.push(interpolated);
        }
      }

      setBarHeights(newBarHeights);

      // Определяем текущую громкость по максимальному бару
      const maxBar = Math.max(...newBarHeights);
      setCurrentVolume(maxBar);

      // Отмечаем, что звук был обнаружен (для проверки через 3 секунды)
      if (maxBar > 10) {
        soundDetectedRef.current = true;
      }

      animationFrameRef.current = requestAnimationFrame(updateBars);
    };

    updateBars();

    return () => {
      if (animationFrameRef.current)
        cancelAnimationFrame(animationFrameRef.current);
    };
  }, [isActive, barCount]);

  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
            size="small"
            options={microphones.map((m) => m.label)}
            defaultOption={selectedMicrophone}
            onSelect={handleSelectMicrophone}
          />
        </div>

        {/* Эквалайзер */}
        <div className="flex flex-col 2xl:gap-[0.833vw] gap-3">
          {/* Визуализация эквалайзера */}
          <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:w-[0.208vw] w-[3px] 2xl:rounded-[0.556vw] rounded-lg transition-all duration-150 ease-out",
                    currentVolume > 10 ? "bg-[#7B60F3]" : "bg-[#D6D6D6]"
                  )}
                  style={{
                    height: `${Math.max(3, height)}px`,
                  }}
                />
              ))}
            </div>

            {/* Статус */}
            {isActive && detectionStatus === "waiting" && (
              <p className="caption-s text-[#7D7D7D] font-medium">
                Говорите...
              </p>
            )}
            {isActive && detectionStatus === "detected" && (
              <p className="caption-s text-[#29AF61] font-medium">Звук есть!</p>
            )}
            {isActive && detectionStatus === "not_detected" && (
              <p className="caption-s text-[#FF4517] font-medium">
                Звук не обнаружен
              </p>
            )}
            {!isActive && (
              <p className="caption-s text-[#FF4517] font-medium">
                Ошибка подключения
              </p>
            )}
          </div>
        </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>

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

export default VoiceCheckModal;
