import semver from "semver"
import { TuiEvent } from "@tui/event"
import { DialogConfirm } from "@tui/ui/dialog-confirm"
import { DialogAlert } from "@tui/ui/dialog-alert"
import { errorMessage } from "@tui/app-config"
import * as SaeeolApp from "@/saeeol/cli/cmd/tui/app"
import { ProviderInstallEvent } from "@/provider/provider-events"
import * as Log from "@saeeol/core/util/log"

type Event = ReturnType<typeof import("@tui/context/event").useEvent>
type Command = ReturnType<typeof import("@tui/component/dialog-command").useCommandDialog>
type Toast = ReturnType<typeof import("@tui/ui/toast").useToast>
type Route = ReturnType<typeof import("@tui/context/route").useRoute>
type KV = ReturnType<typeof import("@tui/context/kv").useKV>
type Dialog = ReturnType<typeof import("@tui/ui/dialog").useDialog>
type SDK = ReturnType<typeof import("@tui/context/sdk").useSDK>
type Exit = ReturnType<typeof import("@tui/context/exit").useExit>

export type EventDeps = {
  event: Event
  command: Command
  toast: Toast
  route: Route
  kv: KV
  dialog: Dialog
  sdk: SDK
  exit: Exit
}

// Session error auto-recovery state
const MAX_SESSION_ERRORS = 3
const ERROR_WINDOW_MS = 60_000 // 1 minute window
const sessionErrors: { time: number; message: string }[] = []

function trimOldErrors() {
  const cutoff = Date.now() - ERROR_WINDOW_MS
  while (sessionErrors.length > 0 && sessionErrors[0].time < cutoff) {
    sessionErrors.shift()
  }
}

export function registerAppEvents(deps: EventDeps) {
  const { event, command, toast, route, kv, dialog, sdk, exit } = deps
  event.on(TuiEvent.CommandExecute.type, (evt) => {
    command.trigger(evt.properties.command)
  })
  event.on(TuiEvent.ToastShow.type, (evt) => {
    toast.show({
      title: evt.properties.title,
      message: evt.properties.message,
      variant: evt.properties.variant,
      duration: evt.properties.duration,
    })
  })
  event.on(TuiEvent.SessionSelect.type, (evt) => {
    route.navigate({
      type: "session",
      sessionID: evt.properties.sessionID,
    })
  })
  event.on("session.deleted", (evt) => {
    if (route.data.type === "session" && route.data.sessionID === evt.properties.info.id) {
      route.navigate({ type: "home" })
      toast.show({
        variant: "info",
        message: "The current session was deleted",
      })
    }
  })
  event.on("session.error", (evt) => {
    const error = evt.properties.error
    if (error && typeof error === "object" && error.name === "MessageAbortedError") return
    if (SaeeolApp.handleSessionError(error, toast)) return
    const message = errorMessage(error)

    // Track error for auto-recovery
    sessionErrors.push({ time: Date.now(), message })
    trimOldErrors()

    if (sessionErrors.length >= MAX_SESSION_ERRORS) {
      // Auto-recovery: too many errors, start fresh session
      sessionErrors.length = 0
      const logPath = Log.file()
      toast.show({
        variant: "warning",
        message: `Session unstable (${MAX_SESSION_ERRORS} errors in 1min). Starting new session.`,
        duration: 5000,
      })
      route.navigate({ type: "home" })
      dialog.clear()
      // If still failing after auto-recovery, show log path
      setTimeout(() => {
        if (logPath) {
          toast.show({
            variant: "info",
            title: "Log File",
            message: logPath,
            duration: 10000,
          })
        }
      }, 6000)
      return
    }

    toast.show({
      variant: "error",
      message,
      duration: 5000,
    })
  })
  event.on("installation.update-available", async (evt) => {
    const version = evt.properties.version
    const skipped = kv.get("skipped_version")
    if (skipped && !semver.gt(version, skipped)) return
    const choice = await DialogConfirm.show(
      dialog,
      `Update Available`,
      `A new release v${version} is available. Would you like to update now?`,
      "skip",
    )
    if (choice === false) {
      kv.set("skipped_version", version)
      return
    }
    if (choice !== true) return
    toast.show({
      variant: "info",
      message: `Updating to v${version}...`,
      duration: 30000,
    })
    const result = await sdk.client.global.upgrade({ target: version })
    if (result.error || !result.data?.success) {
      toast.show({
        variant: "error",
        title: "Update Failed",
        message: "Update failed",
        duration: 10000,
      })
      return
    }
    await DialogAlert.show(
      dialog,
      "Update Complete",
      `Successfully updated to ${SaeeolApp.APP_NAME} v${result.data.version}. Please restart the application.`,
    )
    void exit()
  })

  // Provider SDK 동적 설치 진행 상태 표시
  // subscribe() 사용 + 타입 단언 — SDK Event 유니온에 아직 타입이 없음
  // SDK 재생성(provider-events 레지스트리 포함) 후 타입 안전하게 전환 가능
  event.subscribe((raw: any) => {
    const evt = raw as { type: string; properties: { providerID: string; pkg: string; error?: string } }
    if (evt.type === ProviderInstallEvent.Started.type) {
      toast.show({
        title: "Installing Provider",
        message: `Downloading ${evt.properties.pkg} for ${evt.properties.providerID}...`,
        variant: "info",
        duration: 60000,
      })
    }
    if (evt.type === ProviderInstallEvent.Completed.type) {
      toast.show({
        title: "Provider Ready",
        message: `${evt.properties.providerID} installed successfully`,
        variant: "success",
        duration: 3000,
      })
    }
    if (evt.type === ProviderInstallEvent.Failed.type) {
      toast.show({
        title: "Installation Failed",
        message: `Failed to install ${evt.properties.pkg}: ${evt.properties.error}`,
        variant: "error",
        duration: 10000,
      })
    }
  })
}
