import { app, BrowserWindow, ipcMain, systemPreferences } from "electron"
import type { IpcMainInvokeEvent } from "electron"
import { getStore } from "../store"
import { VOICE_CONFIG_KEY } from "../store-keys"
import { VoiceManager } from "./voice-manager"
import { createVoiceShortcut } from "./voice-shortcut"
import {
  DEFAULT_VOICE_CONFIG,
  apiKeyEnvName,
  resolveApiKey,
  type VoiceConfig,
  type VoiceProvider,
} from "./voice-types"

export type VoiceIpcApi = {
  dispose: () => void
}

export function registerVoiceIpc(): VoiceIpcApi {
  const broadcast = {
    state(state: string) {
      for (const win of BrowserWindow.getAllWindows()) {
        if (win.isDestroyed()) continue
        win.webContents.send("voice:state-changed", state)
      }
    },
    transcription(result: unknown) {
      for (const win of BrowserWindow.getAllWindows()) {
        if (win.isDestroyed()) continue
        win.webContents.send("voice:transcription", result)
      }
    },
    error(error: unknown) {
      for (const win of BrowserWindow.getAllWindows()) {
        if (win.isDestroyed()) continue
        win.webContents.send("voice:error", error)
      }
    },
    captureMode(mode: "native" | "renderer") {
      for (const win of BrowserWindow.getAllWindows()) {
        if (win.isDestroyed()) continue
        win.webContents.send("voice:capture-mode", mode)
      }
    },
  }

  const manager = new VoiceManager({
    onState: (state) => broadcast.state(state),
    onTranscription: (result) => broadcast.transcription(result),
    onError: (error) => broadcast.error(error),
    onCaptureMode: (mode) => broadcast.captureMode(mode),
  })

  manager.setConfig(loadConfig())

  const press = async () => {
    // globalShortcut only fires on keydown, so each press toggles recording.
    if (manager.getState() === "recording") {
      await manager.stop()
      return
    }
    await manager.start()
  }

  const shortcut = createVoiceShortcut({
    getConfig: () => manager.getConfig(),
    onPress: () => {
      void press()
    },
  })
  shortcut.register(manager.getConfig().hotkey)

  ipcMain.handle("voice:get-config", () => {
    const config = manager.getConfig()
    return {
      ...config,
      apiKeyConfigured: !!resolveApiKey(config.provider),
      apiKeyEnv: apiKeyEnvName(config.provider),
    }
  })

  ipcMain.handle("voice:set-config", (_event: IpcMainInvokeEvent, patch: Partial<VoiceConfig>) => {
    const next = manager.setConfig(patch)
    saveConfig(next)
    shortcut.register(next.hotkey)
    return {
      ...next,
      apiKeyConfigured: !!resolveApiKey(next.provider),
      apiKeyEnv: apiKeyEnvName(next.provider),
    }
  })

  ipcMain.handle("voice:start", async () => {
    await ensureMicAccess()
    await manager.start()
    return manager.getState()
  })

  ipcMain.handle("voice:stop", async () => {
    await manager.stop()
    return manager.getState()
  })

  ipcMain.handle("voice:toggle", async () => {
    await ensureMicAccess()
    await manager.toggle()
    return manager.getState()
  })

  ipcMain.handle("voice:get-state", () => manager.getState())

  ipcMain.handle("voice:audio-chunk", (_event: IpcMainInvokeEvent, chunk: ArrayBuffer) => {
    manager.ingestAudio(chunk)
  })

  ipcMain.handle("voice:check-api-key", (_event: IpcMainInvokeEvent, provider?: VoiceProvider) => {
    const selected = provider ?? manager.getConfig().provider
    return {
      provider: selected,
      configured: !!resolveApiKey(selected),
      env: apiKeyEnvName(selected),
    }
  })

  const dispose = () => {
    shortcut.unregister()
    manager.dispose()
  }

  app.once("will-quit", dispose)

  return { dispose }
}

function loadConfig(): VoiceConfig {
  const store = getStore()
  const raw = store.get(VOICE_CONFIG_KEY)
  if (!raw || typeof raw !== "object") return { ...DEFAULT_VOICE_CONFIG }
  const value = raw as Partial<VoiceConfig>
  return {
    enabled: value.enabled ?? DEFAULT_VOICE_CONFIG.enabled,
    provider: isProvider(value.provider) ? value.provider : DEFAULT_VOICE_CONFIG.provider,
    language: typeof value.language === "string" ? value.language : DEFAULT_VOICE_CONFIG.language,
    hotkey: typeof value.hotkey === "string" ? value.hotkey : DEFAULT_VOICE_CONFIG.hotkey,
    commands: value.commands ?? DEFAULT_VOICE_CONFIG.commands,
    autoSend: value.autoSend ?? DEFAULT_VOICE_CONFIG.autoSend,
  }
}

function saveConfig(config: VoiceConfig) {
  getStore().set(VOICE_CONFIG_KEY, config)
}

function isProvider(value: unknown): value is VoiceProvider {
  return value === "deepgram" || value === "groq" || value === "openai-whisper"
}

async function ensureMicAccess() {
  if (process.platform !== "darwin") return
  const status = systemPreferences.getMediaAccessStatus("microphone")
  if (status === "granted") return
  const ok = await systemPreferences.askForMediaAccess("microphone")
  if (!ok) throw new Error("Microphone permission denied")
}
