import type { WidgetRegistryMeta } from 'officerdev';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useQueryClient, useQuery } from '@tanstack/react-query';
import { useClient } from 'hooks/useClient';
import { useAuth } from 'hooks/useAuth';
import { Pause, Play, RotateCcw, Settings, Check, Timer, Volume2, Square } from 'lucide-react';
import { allSounds, categories } from 'sounds';
import type { SoundAsset } from 'sounds';
import { Widget } from '../Widget';

type PomodoroSettings = {
  focusMinutes: number;
  breakMinutes: number;
  longBreakMinutes: number;
  sessionsBeforeLong: number;
  focusEndSound: string | null;
  breakEndSound: string | null;
};

type PomodoroState = {
  settings: PomodoroSettings;
  completedToday: number;
  lastDate: string;
};

const DEFAULT_SETTINGS: PomodoroSettings = {
  focusMinutes: 25,
  breakMinutes: 5,
  longBreakMinutes: 15,
  sessionsBeforeLong: 4,
  focusEndSound: 'confirmation-002',
  breakEndSound: 'notification-pop',
};

const playSound = (name: string | null) => {
  if (!name) return;
  const sound = allSounds.find((s) => s.name === name);
  if (sound) new Audio(sound.dataUri).play();
};

const USER_STATE_KEY = ['USER_STATE'];
const POMODORO_STATE_KEY = 'pomodoro-state';

const todayStr = () => new Date().toISOString().slice(0, 10);

function usePomodoroState() {
  const client = useClient();
  const { isAuthenticated } = useAuth();
  const queryClient = useQueryClient();
  const clientRef = useRef(client);
  clientRef.current = client;

  const { data: userState = {} } = useQuery<Record<string, unknown>>({
    queryKey: USER_STATE_KEY,
    enabled: isAuthenticated,
    queryFn: () => client.get('/user/state'),
    staleTime: Infinity,
  });

  const stored = userState[POMODORO_STATE_KEY] as PomodoroState | undefined;
  const state: PomodoroState = stored
    ? { ...stored, completedToday: stored.lastDate === todayStr() ? stored.completedToday : 0, lastDate: todayStr() }
    : { settings: DEFAULT_SETTINGS, completedToday: 0, lastDate: todayStr() };

  const setState = useCallback(
    (update: PomodoroState | ((prev: PomodoroState) => PomodoroState)) => {
      const currentUserState = queryClient.getQueryData<Record<string, unknown>>(USER_STATE_KEY) ?? {};
      const current = (currentUserState[POMODORO_STATE_KEY] as PomodoroState | undefined) ?? state;
      const next = typeof update === 'function' ? update(current) : update;
      queryClient.setQueryData(USER_STATE_KEY, { ...currentUserState, [POMODORO_STATE_KEY]: next });
      clientRef.current.patch('/user/state', { [POMODORO_STATE_KEY]: next }).catch(() => {});
    },
    [queryClient, state],
  );

  return [state, setState] as const;
}

type Phase = 'focus' | 'break' | 'long-break';

const phaseLabel: Record<Phase, string> = {
  'focus': 'Focus',
  'break': 'Break',
  'long-break': 'Long Break',
};

const phaseColor: Record<Phase, string> = {
  'focus': 'text-destructive',
  'break': 'text-success',
  'long-break': 'text-accent',
};

const formatTime = (seconds: number) => {
  const m = Math.floor(seconds / 60);
  const s = seconds % 60;
  return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
};

const formatSoundName = (name: string) =>
  name
    .split('-')
    .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
    .join(' ');

type SoundPickerProps = {
  label: string;
  value: string | null;
  onChange: (name: string | null) => void;
};

const SoundPicker = ({ label, value, onChange }: SoundPickerProps) => {
  const [open, setOpen] = useState(false);
  const [search, setSearch] = useState('');
  const [activeCategory, setActiveCategory] = useState<string>('all');
  const [previewName, setPreviewName] = useState<string | null>(null);
  const audioRef = useRef<HTMLAudioElement | null>(null);

  const filtered = useMemo(() => {
    const source = activeCategory === 'all' ? allSounds : (categories.find((c) => c.id === activeCategory)?.sounds ?? []);
    if (!search) return source;
    const q = search.toLowerCase();
    return source.filter((s) => s.name.toLowerCase().includes(q));
  }, [search, activeCategory]);

  const preview = (sound: SoundAsset) => {
    if (audioRef.current) {
      audioRef.current.pause();
      audioRef.current = null;
    }
    const audio = new Audio(sound.dataUri);
    audio.addEventListener('ended', () => setPreviewName(null));
    audio.play();
    audioRef.current = audio;
    setPreviewName(sound.name);
  };

  const stopPreview = () => {
    if (audioRef.current) {
      audioRef.current.pause();
      audioRef.current = null;
    }
    setPreviewName(null);
  };

  if (!open) {
    return (
      <div className="flex items-center justify-between gap-2">
        <span className="text-xs text-muted-foreground">{label}</span>
        <button
          type="button"
          className="flex items-center gap-1.5 rounded border border-border px-2 py-1 text-xs hover:bg-accent transition-colors cursor-pointer"
          onClick={() => setOpen(true)}
        >
          <Volume2 size={10} />
          {value ? formatSoundName(value) : 'None'}
        </button>
      </div>
    );
  }

  return (
    <div className="flex flex-col gap-1.5 rounded-md border border-border p-2">
      <div className="flex items-center justify-between">
        <span className="text-xs font-medium">{label}</span>
        <button
          type="button"
          className="text-[10px] text-muted-foreground hover:text-foreground cursor-pointer"
          onClick={() => { stopPreview(); setOpen(false); }}
        >
          Done
        </button>
      </div>
      <input
        type="text"
        placeholder="Search sounds..."
        value={search}
        onChange={(ev) => setSearch(ev.target.value)}
        className="w-full rounded border border-border bg-background px-2 py-1 text-xs outline-none focus:ring-1 focus:ring-ring"
      />
      <div className="flex flex-wrap gap-1">
        {[{ id: 'all', label: 'All' }, ...categories.map((c) => ({ id: c.id, label: c.label }))].map((tab) => (
          <button
            key={tab.id}
            type="button"
            onClick={() => setActiveCategory(tab.id)}
            className={`px-1.5 py-0.5 rounded text-[10px] cursor-pointer transition-colors border ${
              activeCategory === tab.id
                ? 'border-primary bg-primary/10 text-primary'
                : 'border-border text-muted-foreground hover:border-foreground/30'
            }`}
          >
            {tab.label}
          </button>
        ))}
      </div>
      <div className="max-h-32 overflow-y-auto flex flex-col gap-0.5">
        <button
          type="button"
          className={`flex items-center gap-1.5 rounded px-2 py-1 text-left text-xs cursor-pointer transition-colors ${
            value === null ? 'bg-primary/10 text-primary' : 'hover:bg-accent'
          }`}
          onClick={() => { stopPreview(); onChange(null); }}
        >
          None (silent)
        </button>
        {filtered.map((s) => (
          <div key={s.name} className={`flex items-center gap-1 rounded px-2 py-1 text-xs ${value === s.name ? 'bg-primary/10 text-primary' : ''}`}>
            <button
              type="button"
              className="shrink-0 cursor-pointer text-muted-foreground hover:text-foreground"
              onClick={() => previewName === s.name ? stopPreview() : preview(s)}
            >
              {previewName === s.name ? <Square size={10} /> : <Play size={10} />}
            </button>
            <button
              type="button"
              className="flex-1 text-left cursor-pointer hover:text-foreground transition-colors truncate"
              onClick={() => { onChange(s.name); }}
            >
              {formatSoundName(s.name)}
            </button>
          </div>
        ))}
      </div>
    </div>
  );
};

const SettingsPanel = ({ settings, onSave }: { settings: PomodoroSettings; onSave: (s: PomodoroSettings) => void }) => {
  const [draft, setDraft] = useState(settings);

  const numericFields: { key: 'focusMinutes' | 'breakMinutes' | 'longBreakMinutes' | 'sessionsBeforeLong'; label: string }[] = [
    { key: 'focusMinutes', label: 'Focus (min)' },
    { key: 'breakMinutes', label: 'Break (min)' },
    { key: 'longBreakMinutes', label: 'Long break (min)' },
    { key: 'sessionsBeforeLong', label: 'Sessions before long' },
  ];

  return (
    <div className="flex flex-col gap-2 px-4 pb-4">
      {numericFields.map(({ key, label }) => (
        <div key={key} className="flex items-center justify-between gap-2">
          <span className="text-xs text-muted-foreground">{label}</span>
          <input
            type="number"
            min={1}
            max={120}
            value={draft[key]}
            onChange={(ev) => setDraft((d) => ({ ...d, [key]: Math.max(1, Number(ev.target.value)) }))}
            className="w-16 rounded border border-border bg-background px-2 py-1 text-right text-xs outline-none focus:ring-1 focus:ring-ring"
          />
        </div>
      ))}
      <SoundPicker
        label="Focus end sound"
        value={draft.focusEndSound}
        onChange={(name) => setDraft((d) => ({ ...d, focusEndSound: name }))}
      />
      <SoundPicker
        label="Break end sound"
        value={draft.breakEndSound}
        onChange={(name) => setDraft((d) => ({ ...d, breakEndSound: name }))}
      />
      <button
        type="button"
        className="mt-1 flex items-center justify-center gap-1 rounded-md border border-border px-3 py-1.5 text-xs hover:bg-accent transition-colors cursor-pointer"
        onClick={() => onSave(draft)}
      >
        <Check size={12} />
        Save
      </button>
    </div>
  );
};

export const Pomodoro = () => {
  const [pomState, setPomState] = usePomodoroState();
  const { settings, completedToday } = pomState;

  const [phase, setPhase] = useState<Phase>('focus');
  const [running, setRunning] = useState(false);
  const [showSettings, setShowSettings] = useState(false);
  const [sessionCount, setSessionCount] = useState(0);

  const phaseDuration = (p: Phase) => {
    if (p === 'focus') return settings.focusMinutes * 60;
    if (p === 'break') return settings.breakMinutes * 60;
    return settings.longBreakMinutes * 60;
  };

  const [remaining, setRemaining] = useState(phaseDuration('focus'));
  const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);

  const clearTimer = () => {
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
      intervalRef.current = null;
    }
  };

  useEffect(() => {
    if (!running) {
      clearTimer();
      return;
    }

    intervalRef.current = setInterval(() => {
      setRemaining((r) => {
        if (r <= 1) {
          clearTimer();
          setRunning(false);
          if (phase === 'focus') {
            playSound(settings.focusEndSound);
            const newCount = sessionCount + 1;
            setSessionCount(newCount);
            setPomState((s) => ({ ...s, completedToday: s.completedToday + 1, lastDate: todayStr() }));
            const isLongBreak = newCount % settings.sessionsBeforeLong === 0;
            const nextPhase: Phase = isLongBreak ? 'long-break' : 'break';
            setPhase(nextPhase);
            setRemaining(phaseDuration(nextPhase));
          } else {
            playSound(settings.breakEndSound);
            setPhase('focus');
            setRemaining(phaseDuration('focus'));
          }
          return 0;
        }
        return r - 1;
      });
    }, 1000);

    return clearTimer;
  }, [running, phase, sessionCount, settings]);

  const reset = () => {
    clearTimer();
    setRunning(false);
    setPhase('focus');
    setRemaining(phaseDuration('focus'));
  };

  if (showSettings) {
    return (
      <Widget title="Pomodoro">
        <SettingsPanel
          settings={settings}
          onSave={(s) => {
            setPomState((prev) => ({ ...prev, settings: s }));
            setShowSettings(false);
            reset();
          }}
        />
      </Widget>
    );
  }

  const progress = 1 - remaining / phaseDuration(phase);
  const circumference = 2 * Math.PI * 40;
  const dashOffset = circumference * (1 - progress);

  return (
    <Widget title="Pomodoro">
      <div className="flex flex-col items-center gap-3 px-4 pb-4">
        <div className="relative flex items-center justify-center">
          <svg width="100" height="100" className="-rotate-90">
            <circle cx="50" cy="50" r="40" fill="none" stroke="currentColor" strokeWidth="4" className="text-muted/30" />
            <circle
              cx="50"
              cy="50"
              r="40"
              fill="none"
              stroke="currentColor"
              strokeWidth="4"
              strokeDasharray={circumference}
              strokeDashoffset={dashOffset}
              strokeLinecap="round"
              className={phaseColor[phase]}
            />
          </svg>
          <span className="absolute text-xl font-bold tabular-nums">{formatTime(remaining)}</span>
        </div>

        <span className={`text-xs font-medium ${phaseColor[phase]}`}>{phaseLabel[phase]}</span>

        <div className="flex items-center gap-2">
          <button
            type="button"
            className="flex h-8 w-8 items-center justify-center rounded-full border border-border hover:bg-accent transition-colors cursor-pointer"
            onClick={() => setRunning((r) => !r)}
          >
            {running ? <Pause size={14} /> : <Play size={14} />}
          </button>
          <button
            type="button"
            className="flex h-8 w-8 items-center justify-center rounded-full border border-border hover:bg-accent transition-colors cursor-pointer"
            onClick={reset}
          >
            <RotateCcw size={14} />
          </button>
          <button
            type="button"
            className="flex h-8 w-8 items-center justify-center rounded-full border border-border hover:bg-accent transition-colors cursor-pointer"
            onClick={() => setShowSettings(true)}
          >
            <Settings size={14} />
          </button>
        </div>

        <div className="flex items-center gap-1.5 text-xs text-muted-foreground">
          <span>{completedToday} session{completedToday !== 1 ? 's' : ''} today</span>
        </div>
      </div>
    </Widget>
  );
};

export const widgetRegistryMetas: WidgetRegistryMeta[] = [
  { key: 'pomodoro', name: 'Pomodoro', icon: Timer, component: Pomodoro },
];
