import { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';

import {
  clearIncomingRingtone,
  getIncomingRingtone,
  MAX_RINGTONE_BYTES,
  saveIncomingRingtone,
  type StoredRingtone,
} from '../lib/ringtoneStorage';
import { PhoneIcon, SpinnerIcon, TrashIcon } from './icons';

interface Props {
  /** Disable interactions while a parent action is in flight. */
  disabled?: boolean;
}

const BYTES_PER_MB = 1024 * 1024;

// UI for the custom incoming-call ringtone. Single file slot. Upload
// validates size + mime and surfaces errors inline. Preview button plays
// the stored blob through a local <audio> element without touching the
// shared ringtone singleton so we don't interfere with a live call.
export function RingtoneSettings({ disabled = false }: Props) {
  const { t } = useTranslation(['app']);
  const inputRef = useRef<HTMLInputElement | null>(null);
  const previewRef = useRef<HTMLAudioElement | null>(null);
  const previewUrlRef = useRef<string | null>(null);
  const [current, setCurrent] = useState<StoredRingtone | null>(null);
  const [busy, setBusy] = useState(false);
  const [playing, setPlaying] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const refresh = useCallback(async () => {
    try {
      const cur = await getIncomingRingtone();
      setCurrent(cur);
    } catch (err: unknown) {
      console.error('getIncomingRingtone failed', err);
    }
  }, []);

  useEffect(() => {
    void refresh();
  }, [refresh]);

  useEffect(() => {
    // Revoke any preview blob URL when the component unmounts so long-lived
    // pages don't leak memory.
    return () => {
      stopPreview();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  function stopPreview(): void {
    const el = previewRef.current;
    if (el) {
      try {
        el.pause();
      } catch {
        /* ignore */
      }
      el.src = '';
    }
    previewRef.current = null;
    if (previewUrlRef.current) {
      URL.revokeObjectURL(previewUrlRef.current);
      previewUrlRef.current = null;
    }
    setPlaying(false);
  }

  async function handleFile(file: File): Promise<void> {
    setError(null);
    setBusy(true);
    try {
      await saveIncomingRingtone(file);
      await refresh();
    } catch (err: unknown) {
      const code = err instanceof Error ? err.message : 'upload_failed';
      if (code === 'ringtone_too_large') {
        setError(
          t('app:settings.ringtone_error_too_large', {
            defaultValue: 'Datei zu groß (max {{max}} MB).',
            max: MAX_RINGTONE_BYTES / BYTES_PER_MB,
          }),
        );
      } else if (code === 'ringtone_not_audio') {
        setError(
          t('app:settings.ringtone_error_not_audio', {
            defaultValue: 'Nur Audio-Dateien werden unterstützt.',
          }),
        );
      } else {
        setError(
          t('app:settings.ringtone_error_generic', {
            defaultValue: 'Ringtone konnte nicht gespeichert werden.',
          }),
        );
      }
    } finally {
      setBusy(false);
      if (inputRef.current) inputRef.current.value = '';
    }
  }

  async function handleReset(): Promise<void> {
    setError(null);
    setBusy(true);
    stopPreview();
    try {
      await clearIncomingRingtone();
      await refresh();
    } catch (err: unknown) {
      console.error('clearIncomingRingtone failed', err);
    } finally {
      setBusy(false);
    }
  }

  function handlePreview(): void {
    if (!current) return;
    if (playing) {
      stopPreview();
      return;
    }
    const url = URL.createObjectURL(current.blob);
    const el = new Audio(url);
    el.loop = false;
    el.volume = 0.85;
    el.onended = () => stopPreview();
    el.onerror = () => {
      setError(
        t('app:settings.ringtone_error_play', {
          defaultValue: 'Ringtone konnte nicht abgespielt werden.',
        }),
      );
      stopPreview();
    };
    el.play().catch(() => {
      setError(
        t('app:settings.ringtone_error_play', {
          defaultValue: 'Ringtone konnte nicht abgespielt werden.',
        }),
      );
      stopPreview();
    });
    previewRef.current = el;
    previewUrlRef.current = url;
    setPlaying(true);
  }

  const hasCustom = current !== null;
  const sizeMb = current ? (current.blob.size / BYTES_PER_MB).toFixed(2) : null;
  const interactionsDisabled = disabled || busy;

  return (
    <div className="flex flex-col gap-3">
      <div className="flex items-start justify-between gap-4">
        <div className="min-w-0 flex-1">
          <div className="flex items-center gap-2 text-sm font-medium text-fg">
            <PhoneIcon className="h-4 w-4 text-fg-muted" />
            {t('app:settings.ringtone_incoming', { defaultValue: 'Eingehender Anruf' })}
          </div>
          <div className="mt-1 text-xs text-fg-muted">
            {hasCustom && current
              ? t('app:settings.ringtone_custom_active', {
                  defaultValue: '{{name}} · {{size}} MB',
                  name: current.filename,
                  size: sizeMb,
                })
              : t('app:settings.ringtone_default_active', {
                  defaultValue: 'Standard-Klingelton (Doppelton)',
                })}
          </div>
        </div>

        <div className="flex shrink-0 items-center gap-2">
          {hasCustom && (
            <button
              type="button"
              onClick={handlePreview}
              disabled={interactionsDisabled}
              className="cursor-pointer rounded-lg border border-line bg-surface-3 px-3 py-1.5 text-xs font-semibold text-fg transition hover:brightness-95 disabled:cursor-not-allowed disabled:opacity-50"
            >
              {playing
                ? t('app:settings.ringtone_stop', { defaultValue: 'Stop' })
                : t('app:settings.ringtone_preview', { defaultValue: 'Vorhören' })}
            </button>
          )}
          <input
            ref={inputRef}
            type="file"
            accept="audio/*"
            className="hidden"
            onChange={(e) => {
              const f = e.target.files?.[0];
              if (f) void handleFile(f);
            }}
          />
          <button
            type="button"
            onClick={() => inputRef.current?.click()}
            disabled={interactionsDisabled}
            className="inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-accent px-3 py-1.5 text-xs font-semibold text-accent-fg transition hover:brightness-110 disabled:cursor-not-allowed disabled:opacity-50"
          >
            {busy && <SpinnerIcon className="h-3.5 w-3.5" />}
            <span>
              {hasCustom
                ? t('app:settings.ringtone_replace', { defaultValue: 'Ersetzen' })
                : t('app:settings.ringtone_upload', { defaultValue: 'Hochladen' })}
            </span>
          </button>
          {hasCustom && (
            <button
              type="button"
              onClick={() => void handleReset()}
              disabled={interactionsDisabled}
              aria-label={t('app:settings.ringtone_reset', { defaultValue: 'Zurücksetzen' })}
              title={t('app:settings.ringtone_reset', { defaultValue: 'Zurücksetzen' })}
              className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg border border-rose-500/40 bg-rose-500/10 text-rose-600 transition hover:bg-rose-500/20 disabled:cursor-not-allowed disabled:opacity-50 dark:text-rose-300"
            >
              <TrashIcon className="h-4 w-4" />
            </button>
          )}
        </div>
      </div>

      {error && (
        <p className="rounded-md border border-rose-500/30 bg-rose-500/10 px-3 py-2 text-xs text-rose-600 dark:text-rose-300">
          {error}
        </p>
      )}

      <p className="text-[11px] text-fg-muted">
        {t('app:settings.ringtone_hint', {
          defaultValue:
            'MP3, WAV, OGG oder M4A bis 8 MB. Nur für eingehende Anrufe — ausgehend bleibt der Standard.',
        })}
      </p>
    </div>
  );
}
