import { createContext, useContext, useEffect, useRef, useState, useCallback } from "react";
import type { ReactNode } from "react";
import type { Recording } from "@shared/schema";
import { audioObjectUrl, getAudio } from "./local-db";

type Theme = "dark" | "light";

interface PlayerState {
  recording: Recording | null;
  position: number; // seconds
  playing: boolean;
}

interface Snackbar {
  id: number;
  message: string;
  actionLabel?: string;
  onAction?: () => void;
  timeoutId?: number;
}

interface AppCtx {
  theme: Theme;
  setTheme: (t: Theme) => void;
  player: PlayerState;
  openPlayer: (rec: Recording, autoplay?: boolean) => void;
  togglePlay: () => void;
  closePlayer: () => void;
  seekPlayer: (sec: number) => void;
  snackbar: Snackbar | null;
  showSnackbar: (s: Omit<Snackbar, "id" | "timeoutId">, durationMs?: number) => void;
  dismissSnackbar: () => void;
}

const Ctx = createContext<AppCtx | null>(null);

// Helper-Exporte für Komponenten, die Download/Share brauchen.
export async function getRecordingBlob(rec: Recording): Promise<Blob | null> {
  return getAudio(rec.id);
}

export async function getRecordingObjectUrl(rec: Recording): Promise<string | null> {
  return audioObjectUrl(rec.id);
}

// Legacy-kompatible Aliasse (alte Imports nicht brechen).
export function audioUrl(_rec: Recording): string {
  return ""; // async — Komponenten sollten getRecordingObjectUrl benutzen
}
export function downloadUrl(_rec: Recording): string {
  return "";
}

export function AppProvider({ children }: { children: ReactNode }) {
  const [theme, setThemeState] = useState<Theme>("dark");
  const [player, setPlayer] = useState<PlayerState>({ recording: null, position: 0, playing: false });
  const [snackbar, setSnackbar] = useState<Snackbar | null>(null);
  const audioRef = useRef<HTMLAudioElement | null>(null);

  // Globales <audio>-Element.
  useEffect(() => {
    const a = new Audio();
    a.preload = "auto";
    audioRef.current = a;
    const onTime = () => setPlayer((p) => ({ ...p, position: a.currentTime }));
    const onEnd = () => setPlayer((p) => ({ ...p, position: 0, playing: false }));
    const onPlay = () => setPlayer((p) => ({ ...p, playing: true }));
    const onPause = () => setPlayer((p) => ({ ...p, playing: false }));
    a.addEventListener("timeupdate", onTime);
    a.addEventListener("ended", onEnd);
    a.addEventListener("play", onPlay);
    a.addEventListener("pause", onPause);
    return () => {
      a.removeEventListener("timeupdate", onTime);
      a.removeEventListener("ended", onEnd);
      a.removeEventListener("play", onPlay);
      a.removeEventListener("pause", onPause);
      a.pause();
      audioRef.current = null;
    };
  }, []);

  // Theme-Klasse auf <html>.
  useEffect(() => {
    const root = document.documentElement;
    if (theme === "dark") root.classList.add("dark");
    else root.classList.remove("dark");
    root.style.setProperty("color-scheme", theme);
  }, [theme]);

  const setTheme = useCallback((t: Theme) => setThemeState(t), []);

  const openPlayer = useCallback((rec: Recording, autoplay = false) => {
    const a = audioRef.current;
    if (!a) return;
    setPlayer({ recording: rec, position: 0, playing: false });
    // Blob asynchron laden, dann src setzen.
    audioObjectUrl(rec.id).then((url) => {
      if (!url || !audioRef.current) return;
      if (audioRef.current.src !== url) {
        audioRef.current.src = url;
      }
      audioRef.current.currentTime = 0;
      if (autoplay) {
        audioRef.current.play().catch(() => {});
      }
    });
  }, []);

  const togglePlay = useCallback(() => {
    const a = audioRef.current;
    if (!a) return;
    if (a.paused) a.play().catch(() => {});
    else a.pause();
  }, []);

  const closePlayer = useCallback(() => {
    const a = audioRef.current;
    if (a) { a.pause(); a.src = ""; }
    setPlayer({ recording: null, position: 0, playing: false });
  }, []);

  const seekPlayer = useCallback((sec: number) => {
    const a = audioRef.current;
    if (!a) return;
    const dur = a.duration || 0;
    const clamped = Math.max(0, Math.min(sec, dur || sec));
    a.currentTime = clamped;
    setPlayer((p) => ({ ...p, position: clamped }));
  }, []);

  const dismissSnackbar = useCallback(() => {
    setSnackbar((s) => {
      if (s?.timeoutId) clearTimeout(s.timeoutId);
      return null;
    });
  }, []);

  const showSnackbar = useCallback((s: Omit<Snackbar, "id" | "timeoutId">, durationMs = 5000) => {
    setSnackbar((prev) => {
      if (prev?.timeoutId) clearTimeout(prev.timeoutId);
      const id = Date.now();
      const timeoutId = window.setTimeout(() => {
        setSnackbar((cur) => (cur?.id === id ? null : cur));
      }, durationMs);
      return { ...s, id, timeoutId };
    });
  }, []);

  return (
    <Ctx.Provider
      value={{
        theme,
        setTheme,
        player,
        openPlayer,
        togglePlay,
        closePlayer,
        seekPlayer,
        snackbar,
        showSnackbar,
        dismissSnackbar,
      }}
    >
      {children}
    </Ctx.Provider>
  );
}

export function useApp() {
  const ctx = useContext(Ctx);
  if (!ctx) throw new Error("useApp must be inside AppProvider");
  return ctx;
}
