import { useDevices } from '@/components/devices-provider/hooks/use-devices';
import { Button } from '@/components/ui/button';
import { Group } from '@/components/ui/group';
import { LoadingCard } from '@/components/ui/loading-card';
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue
} from '@/components/ui/select';
import { Slider } from '@/components/ui/slider';
import { Switch } from '@/components/ui/switch';
import { closeServerScreens } from '@/features/server-screens/actions';
import { useCurrentVoiceChannelId } from '@/features/server/channels/hooks';
import { useVoice } from '@/features/server/voice/hooks';
import { useForm } from '@/hooks/use-form';
import { Resolution } from '@/types';
import { Download, Trash2, CheckCircle, XCircle, Loader2 } from 'lucide-react';
import { memo, useCallback, useEffect, useState } from 'react';
import { toast } from 'sonner';
import { useAvailableDevices } from './hooks/use-available-devices';
import ResolutionFpsControl from './resolution-fps-control';

const DEFAULT_NAME = 'default';
type TMicProcessingSnapshot = {
  active: boolean;
  chain: string;
  note: string;
  updatedAt?: number;
};

const MIC_STATUS_STORAGE_KEY = 'pulse.micProcessingStatus';

const loadStoredMicStatus = (): TMicProcessingSnapshot | null => {
  try {
    const raw = localStorage.getItem(MIC_STATUS_STORAGE_KEY);
    if (!raw) return null;
    const parsed = JSON.parse(raw) as TMicProcessingSnapshot;
    if (
      typeof parsed?.active !== 'boolean' ||
      typeof parsed?.chain !== 'string' ||
      typeof parsed?.note !== 'string'
    ) {
      return null;
    }
    return parsed;
  } catch {
    return null;
  }
};

const getConfiguredMicChain = (values: {
  noiseSuppressionDeepFilterNet?: boolean;
  noiseSuppressionRnnoise?: boolean;
  keyboardSuppression?: boolean;
  noiseSuppressionEnhanced?: boolean;
  echoCancellation?: boolean;
  noiseSuppression?: boolean;
  autoGainControl?: boolean;
}) => {
  const chain: string[] = [];
  if (values.noiseSuppressionDeepFilterNet) chain.push('DeepFilterNet');
  if (values.noiseSuppressionRnnoise) chain.push('RNNoise');
  if (values.keyboardSuppression) chain.push('Keyboard Gate');
  if (values.noiseSuppressionEnhanced) chain.push('Enhanced (browser)');
  if (
    !chain.length &&
    (values.echoCancellation || values.noiseSuppression || values.autoGainControl)
  ) {
    chain.push('Browser DSP');
  }
  return chain.length ? chain.join(' + ') : 'none';
};

const Devices = memo(() => {
  const currentVoiceChannelId = useCurrentVoiceChannelId();
  const {
    inputDevices,
    videoDevices,
    loading: availableDevicesLoading
  } = useAvailableDevices();
  const { micProcessingStatus } = useVoice();
  const { devices, saveDevices, loading: devicesLoading } = useDevices();
  const { values, onChange } = useForm(devices);
  const [storedMicStatus, setStoredMicStatus] =
    useState<TMicProcessingSnapshot | null>(null);

  useEffect(() => {
    setStoredMicStatus(loadStoredMicStatus());

    const onStatus = (event: Event) => {
      const custom = event as CustomEvent<TMicProcessingSnapshot>;
      if (!custom.detail) return;
      setStoredMicStatus(custom.detail);
    };

    window.addEventListener('pulse:mic-processing-status', onStatus as EventListener);
    return () => {
      window.removeEventListener(
        'pulse:mic-processing-status',
        onStatus as EventListener
      );
    };
  }, []);

  const configuredChain = getConfiguredMicChain(values);
  const runtimeUnavailable =
    !micProcessingStatus.active &&
    micProcessingStatus.chain === 'none' &&
    micProcessingStatus.note === 'Runtime status unavailable on this screen';
  const effectiveStatus = runtimeUnavailable && storedMicStatus
    ? storedMicStatus
    : micProcessingStatus;

  const handleEnhancedToggle = useCallback(
    (checked: boolean) => {
      onChange('noiseSuppressionEnhanced', checked);
      if (checked) {
        onChange('noiseSuppressionRnnoise', false);
        onChange('noiseSuppressionDeepFilterNet', false);
      }
    },
    [onChange]
  );

  const handleRnnoiseToggle = useCallback(
    (checked: boolean) => {
      onChange('noiseSuppressionRnnoise', checked);
      if (checked) {
        onChange('noiseSuppressionEnhanced', false);
        onChange('noiseSuppressionDeepFilterNet', false);
        onChange('echoCancellation', false);
        onChange('autoGainControl', false);
        onChange('noiseSuppression', false);
      }
    },
    [onChange]
  );

  const handleDeepFilterToggle = useCallback(
    (checked: boolean) => {
      onChange('noiseSuppressionDeepFilterNet', checked);
      if (checked) {
        onChange('noiseSuppressionEnhanced', false);
        onChange('noiseSuppressionRnnoise', false);
        onChange('echoCancellation', false);
        onChange('autoGainControl', false);
        onChange('noiseSuppression', false);
      }
    },
    [onChange]
  );

  const handleEchoCancellationToggle = useCallback(
    (checked: boolean) => {
      onChange('echoCancellation', checked);
      if (checked) {
        onChange('noiseSuppressionRnnoise', false);
        onChange('noiseSuppressionDeepFilterNet', false);
      }
    },
    [onChange]
  );

  const handleNoiseSuppressionToggle = useCallback(
    (checked: boolean) => {
      onChange('noiseSuppression', checked);
      if (checked) {
        onChange('noiseSuppressionRnnoise', false);
        onChange('noiseSuppressionDeepFilterNet', false);
      }
    },
    [onChange]
  );

  const handleAutoGainControlToggle = useCallback(
    (checked: boolean) => {
      onChange('autoGainControl', checked);
      if (checked) {
        onChange('noiseSuppressionRnnoise', false);
        onChange('noiseSuppressionDeepFilterNet', false);
      }
    },
    [onChange]
  );

  const hasWasmSuppression =
    !!values.noiseSuppressionRnnoise || !!values.noiseSuppressionDeepFilterNet;

  const saveDeviceSettings = useCallback(() => {
    saveDevices(values);
    toast.success('Device settings saved');
  }, [saveDevices, values]);

  if (availableDevicesLoading || devicesLoading) {
    return <LoadingCard className="h-[600px]" />;
  }

  return (
    <div className="space-y-4">
        <Group label="Microphone">
          <Select
            onValueChange={(value) => onChange('microphoneId', value)}
            value={values.microphoneId}
          >
            <SelectTrigger className="w-[500px]">
              <SelectValue placeholder="Select the input device" />
            </SelectTrigger>
            <SelectContent>
              <SelectGroup>
                {inputDevices.map((device) => (
                  <SelectItem
                    key={device?.deviceId}
                    value={device?.deviceId || DEFAULT_NAME}
                  >
                    {device?.label.trim() || 'Default Microphone'}
                  </SelectItem>
                ))}
              </SelectGroup>
            </SelectContent>
          </Select>

          <div className="flex gap-8">
            <Group label="Echo cancellation">
              <Switch
                checked={!!values.echoCancellation}
                onCheckedChange={handleEchoCancellationToggle}
              />
            </Group>

            <Group label="Noise suppression">
              <Switch
                checked={!!values.noiseSuppression}
                onCheckedChange={handleNoiseSuppressionToggle}
              />
            </Group>

            {hasWasmSuppression ? (
              <div className="text-xs text-muted-foreground max-w-xs">
                Browser echo/noise/AGC are disabled while RNNoise or DeepFilterNet is active.
              </div>
            ) : null}

            <Group label="Enhanced noise suppression">
              <Switch
                checked={!!values.noiseSuppressionEnhanced}
                onCheckedChange={handleEnhancedToggle}
              />
            </Group>

            <Group label="RNNoise (WASM)">
              <Switch
                checked={!!values.noiseSuppressionRnnoise}
                onCheckedChange={handleRnnoiseToggle}
              />
            </Group>

            <Group label="DeepFilterNet (WASM)">
              <Switch
                checked={!!values.noiseSuppressionDeepFilterNet}
                onCheckedChange={handleDeepFilterToggle}
              />
            </Group>

            <Group
              label="Keyboard suppression"
              description="Speech-safe gate to reduce key clicks while keeping voice pickup."
            >
              <Switch
                checked={!!values.keyboardSuppression}
                onCheckedChange={(checked) =>
                  onChange('keyboardSuppression', checked)
                }
              />
            </Group>

            <Group
              label="Voice sensitivity"
              description="Higher values pick up quieter speech; lower values suppress more background."
            >
              <Slider
                min={0}
                max={100}
                step={1}
                value={[values.voiceSensitivity ?? 70]}
                onValueChange={([sensitivity]) =>
                  onChange('voiceSensitivity', sensitivity)
                }
                rightSlot={
                  <span className="text-xs text-muted-foreground w-9 text-right">
                    {Math.round(values.voiceSensitivity ?? 70)}
                  </span>
                }
              />
            </Group>

            <Group
              label="Mic Processing Status"
              description="Runtime status from the active voice pipeline."
            >
              <div className="rounded-md border border-border px-3 py-2 text-xs leading-5">
                <div>
                  <strong>Configured:</strong> {configuredChain}
                </div>
                <div>
                  <strong>Runtime chain:</strong> {effectiveStatus.chain}
                </div>
                <div>
                  <strong>Active:</strong> {effectiveStatus.active ? 'yes' : 'no'}
                </div>
                <div>
                  <strong>Note:</strong> {runtimeUnavailable && !storedMicStatus
                    ? 'Open this panel while connected to voice to read live processing status.'
                    : effectiveStatus.note}
                </div>
              </div>
            </Group>

            <Group label="Automatic gain control">
              <Switch
                checked={!!values.autoGainControl}
                onCheckedChange={handleAutoGainControlToggle}
              />
            </Group>
          </div>
        </Group>

        <Group label="Webcam">
          <Select
            onValueChange={(value) => onChange('webcamId', value)}
            value={values.webcamId}
          >
            <SelectTrigger className="w-[500px]">
              <SelectValue placeholder="Select the input device" />
            </SelectTrigger>
            <SelectContent>
              <SelectGroup>
                {videoDevices.map((device) => (
                  <SelectItem
                    key={device?.deviceId}
                    value={device?.deviceId || DEFAULT_NAME}
                  >
                    {device?.label.trim() || 'Default Webcam'}
                  </SelectItem>
                ))}
              </SelectGroup>
            </SelectContent>
          </Select>

          <ResolutionFpsControl
            framerate={values.webcamFramerate}
            resolution={values.webcamResolution}
            onFramerateChange={(value) => onChange('webcamFramerate', value)}
            onResolutionChange={(value) =>
              onChange('webcamResolution', value as Resolution)
            }
          />
        </Group>

        <Group label="Screen Sharing" description={currentVoiceChannelId ? 'Screen sharing settings take effect on your next share.' : undefined}>
          <ResolutionFpsControl
            framerate={values.screenFramerate}
            resolution={values.screenResolution}
            onFramerateChange={(value) => onChange('screenFramerate', value)}
            onResolutionChange={(value) =>
              onChange('screenResolution', value as Resolution)
            }
          />

          <div className="flex items-center gap-2">
            <Group label="Audio Bitrate">
              <Select
                value={(values.screenAudioBitrate ?? 128).toString()}
                onValueChange={(value) =>
                  onChange('screenAudioBitrate', +value)
                }
              >
                <SelectTrigger className="w-[160px]">
                  <SelectValue placeholder="Audio bitrate" />
                </SelectTrigger>
                <SelectContent>
                  <SelectGroup>
                    <SelectItem value="64">64 kbps</SelectItem>
                    <SelectItem value="96">96 kbps</SelectItem>
                    <SelectItem value="128">128 kbps</SelectItem>
                    <SelectItem value="192">192 kbps</SelectItem>
                    <SelectItem value="256">256 kbps</SelectItem>
                    <SelectItem value="320">320 kbps</SelectItem>
                  </SelectGroup>
                </SelectContent>
              </Select>
            </Group>
          </div>
        </Group>
        {/* macOS Audio Driver — only shown in Electron on macOS */}
        <MacOSAudioDriverSection />

        <div className="flex justify-end gap-2 pt-4">
          <Button variant="outline" onClick={closeServerScreens}>
            Cancel
          </Button>
          <Button onClick={saveDeviceSettings}>Save Changes</Button>
        </div>
    </div>
  );
});

/** macOS system audio capture driver management section */
const MacOSAudioDriverSection = memo(() => {
  const isMacElectron = window.pulseDesktop?.platform === 'darwin';
  const [driverStatus, setDriverStatus] = useState<{
    supported: boolean;
    fileInstalled: boolean;
    active: boolean;
  } | null>(null);
  const [loading, setLoading] = useState(false);

  const refreshStatus = useCallback(async () => {
    if (!window.pulseDesktop?.audioDriver) return;
    try {
      const status = await window.pulseDesktop.audioDriver.getStatus();
      setDriverStatus(status);
    } catch {
      setDriverStatus(null);
    }
  }, []);

  useEffect(() => {
    if (isMacElectron) refreshStatus();
  }, [isMacElectron, refreshStatus]);

  if (!isMacElectron || !driverStatus?.supported) return null;

  const handleInstall = async () => {
    setLoading(true);
    try {
      const result = await window.pulseDesktop!.audioDriver.install();
      if (result.success) {
        toast.success('Audio driver installed — "Pulse Audio" device is now available');
      } else if (result.error) {
        toast.error(result.error);
      }
    } catch {
      toast.error('Failed to install audio driver');
    } finally {
      setLoading(false);
      refreshStatus();
    }
  };

  const handleUninstall = async () => {
    setLoading(true);
    try {
      const result = await window.pulseDesktop!.audioDriver.uninstall();
      if (result.success) {
        toast.success('Audio driver uninstalled');
      } else if (result.error) {
        toast.error(result.error);
      }
    } catch {
      toast.error('Failed to uninstall audio driver');
    } finally {
      setLoading(false);
      refreshStatus();
    }
  };

  return (
    <Group label="System Audio Capture (macOS)">
      <p className="text-sm text-muted-foreground">
        Share system audio during screen sharing. Requires a virtual audio driver
        installed to <code>/Library/Audio/Plug-Ins/HAL/</code>.
      </p>
      <div className="flex items-center gap-3">
        <div className="flex items-center gap-2">
          {driverStatus.active ? (
            <>
              <CheckCircle className="h-4 w-4 text-green-500" />
              <span className="text-sm text-green-500">Driver installed and active</span>
            </>
          ) : driverStatus.fileInstalled ? (
            <>
              <Loader2 className="h-4 w-4 text-yellow-500 animate-spin" />
              <span className="text-sm text-yellow-500">Driver installed but not active (restart coreaudiod)</span>
            </>
          ) : (
            <>
              <XCircle className="h-4 w-4 text-muted-foreground" />
              <span className="text-sm text-muted-foreground">Driver not installed</span>
            </>
          )}
        </div>
      </div>
      <div className="flex gap-2">
        {!driverStatus.active && (
          <Button
            size="sm"
            onClick={handleInstall}
            disabled={loading}
          >
            {loading ? (
              <Loader2 className="mr-2 h-4 w-4 animate-spin" />
            ) : (
              <Download className="mr-2 h-4 w-4" />
            )}
            Install Driver
          </Button>
        )}
        {(driverStatus.fileInstalled || driverStatus.active) && (
          <Button
            size="sm"
            variant="destructive"
            onClick={handleUninstall}
            disabled={loading}
          >
            {loading ? (
              <Loader2 className="mr-2 h-4 w-4 animate-spin" />
            ) : (
              <Trash2 className="mr-2 h-4 w-4" />
            )}
            Uninstall Driver
          </Button>
        )}
      </div>
    </Group>
  );
});

export { Devices };
