import { useEffect, useRef, useState } from 'react';

import { IoSend } from 'react-icons/io5';
import { LuTrash2 } from 'react-icons/lu';

import Sheet from '../../../ui/Sheet';

type Props = {
  onCancel: () => void;
  onSend: (audio: Blob) => void;
};

const BAR_COUNT = 42;
const IDLE_HEIGHTS = () =>
  Array.from({ length: BAR_COUNT }, (_, i) => {
    const t = i / BAR_COUNT;
    return 5 + Math.sin(t * Math.PI * 3) * 3;
  });

const RecordingSheet = ({ onCancel, onSend }: Props) => {
  const [seconds, setSeconds] = useState(0);
  const [opened, setOpened] = useState(false);
  const [levels, setLevels] = useState<number[]>(IDLE_HEIGHTS);

  const audioBlob = useRef<Blob | null>(null);
  const mediaRecorder = useRef<MediaRecorder | null>(null);
  const streamRef = useRef<MediaStream | null>(null);
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
  const chunks = useRef<Blob[]>([]);
  const inited = useRef(false);

  const audioContextRef = useRef<AudioContext | null>(null);
  const analyserRef = useRef<AnalyserNode | null>(null);
  const rafRef = useRef<number>(0);
  const dataArrayRef = useRef<Uint8Array | null>(null);

  useEffect(() => {
    const t = setTimeout(() => setOpened(true), 50);
    return () => clearTimeout(t);
  }, []);

  useEffect(() => {
    if (inited.current) return;
    inited.current = true;

    const startMeter = (stream: MediaStream) => {
      const AC =
        window.AudioContext ||
        (window as unknown as { webkitAudioContext?: typeof AudioContext })
          .webkitAudioContext;
      if (!AC) return;

      const ctx = new AC();
      audioContextRef.current = ctx;
      void ctx.resume().catch(() => {});

      const source = ctx.createMediaStreamSource(stream);
      const analyser = ctx.createAnalyser();
      analyser.fftSize = 256;
      analyser.smoothingTimeConstant = 0.72;
      analyserRef.current = analyser;
      source.connect(analyser);

      const bufferLength = analyser.frequencyBinCount;
      const dataArray = new Uint8Array(new ArrayBuffer(bufferLength));
      dataArrayRef.current = dataArray;

      const tick = () => {
        const a = analyserRef.current;
        const data = dataArrayRef.current;
        if (!a || !data) return;

        a.getByteFrequencyData(data as Uint8Array<ArrayBuffer>);
        const step = Math.max(1, Math.floor(bufferLength / BAR_COUNT));
        const next: number[] = [];
        for (let i = 0; i < BAR_COUNT; i++) {
          let sum = 0;
          const start = i * step;
          for (let j = 0; j < step && start + j < bufferLength; j++) {
            sum += data[start + j] ?? 0;
          }
          const avg = sum / step;
          const h = 4 + (avg / 255) * 26;
          next.push(Math.max(4, Math.min(30, h)));
        }
        setLevels(next);
        rafRef.current = requestAnimationFrame(tick);
      };
      rafRef.current = requestAnimationFrame(tick);
    };

    const start = async () => {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
        streamRef.current = stream;

        const recorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });
        recorder.ondataavailable = (e) => chunks.current.push(e.data);
        recorder.onstop = () => {
          const blob = new Blob(chunks.current, { type: 'audio/webm' });
          audioBlob.current = blob;
          chunks.current = [];
        };

        recorder.start();
        mediaRecorder.current = recorder;
        timerRef.current = setInterval(() => setSeconds((s) => s + 1), 1000);
        startMeter(stream);
      } catch (err) {
        console.error('🎙 Доступ к микрофону отклонён', err);
        onCancel();
      }
    };

    start();

    return () => {
      if (timerRef.current) clearInterval(timerRef.current);
      if (rafRef.current) cancelAnimationFrame(rafRef.current);
      void audioContextRef.current?.close();
    };
  }, [onCancel]);

  const stopAll = () => {
    if (rafRef.current) cancelAnimationFrame(rafRef.current);
    rafRef.current = 0;
    void audioContextRef.current?.close();
    audioContextRef.current = null;
    analyserRef.current = null;
    dataArrayRef.current = null;

    mediaRecorder.current?.stop();
    streamRef.current?.getTracks().forEach((t) => t.stop());
    if (timerRef.current) clearInterval(timerRef.current);
  };

  const handleSend = () => {
    stopAll();
    setTimeout(() => {
      if (audioBlob.current) onSend(audioBlob.current);
    }, 200);
  };

  const handleCancel = () => {
    stopAll();
    onCancel();
  };

  const fmt = (s: number) =>
    `${Math.floor(s / 60)}:${(s % 60).toString().padStart(2, '0')}`;

  return (
    <Sheet opened={opened} onClose={handleCancel}>
      <div
        className="mx-3 mb-2 mt-1 flex items-center gap-2 rounded-[22px] bg-[var(--tg-bg-secondary)]
          px-2 py-2 pl-3 pr-2 shadow-[0_1px_0_rgba(0,0,0,0.06)]"
      >
        <button
          type="button"
          onClick={handleCancel}
          className="flex h-11 w-11 shrink-0 items-center justify-center rounded-full
            bg-[var(--tg-bg)] text-[var(--tg-text-secondary)] tg-press
            shadow-sm"
          aria-label="Удалить запись"
        >
          <LuTrash2 size={22} strokeWidth={2} />
        </button>

        <div className="flex min-w-0 flex-1 flex-col gap-1">
          <div className="flex h-8 items-end justify-center gap-[2px] px-1">
            {levels.map((h, i) => (
              <div
                key={i}
                className="w-[2.5px] shrink-0 rounded-full bg-[var(--tg-accent)]"
                style={{ height: `${h}px`, opacity: 0.35 + (h / 30) * 0.65 }}
              />
            ))}
          </div>
          <div className="flex items-center justify-center gap-1.5">
            <span
              className="inline-block h-2 w-2 shrink-0 rounded-full bg-[#e53935] shadow-[0_0_0_2px_rgba(229,57,53,0.25)]"
              aria-hidden
            />
            <span className="font-mono text-[13px] tabular-nums text-[var(--tg-text-primary)]">
              {fmt(seconds)}
            </span>
          </div>
        </div>

        <button
          type="button"
          onClick={handleSend}
          className="flex h-11 w-11 shrink-0 items-center justify-center rounded-full
            bg-[var(--tg-accent)] text-white tg-press shadow-md"
          aria-label="Отправить голосовое"
        >
          <IoSend size={20} className="-ml-0.5" />
        </button>
      </div>
      <p className="px-4 pb-4 text-center text-[12px] text-[var(--tg-text-secondary)]">
        Запись… Отправьте или удалите сообщение
      </p>
    </Sheet>
  );
};

export default RecordingSheet;
