import type { Nuxt } from '@nuxt/schema'
import type { ShallowRef } from 'vue'

declare module 'nuxt/schema' {
  interface PublicRuntimeConfig {
    desktop: DesktopConfig
  }
}

declare global {
  interface IApplicationManager {
  apps: Map<string, IApplicationController>

  get appsEntries(): Reactive<ApplicationEntry[]>

  get appsRunning(): Reactive<IApplicationController[]>

  get windowsOpened(): Reactive<Map<string, IWindowController>>

  get appCategories(): string[]

  get appsByCategory(): { [category: string]: IApplicationController[] }

  defineApp(
    id: string,
    config: ApplicationConfig,
  ): Promise<IApplicationController>

  closeApp(id: string): void

  launchAppEntry(
    id: string,
    entry: string,
  ): Promise<IApplicationController | undefined | void>
  execAppCommand(
    id: string,
    command: string,
    meta: undefined | any,
  ): Promise<CommandOutput | void>

  isAppDefined(id: string): boolean

  isAppRunning(id: string): boolean
}

type ApplicationCommand = (app: IApplicationController, args: any) => void

interface ApplicationConfig {
  id: string
  title: string
  icon?: string
  category?: string
  version?: string
  description?: string
  provides?: ApplicationConfigProvide
  singleton?: boolean
  meta?: IApplicationMeta
  permissions?: ApplicationPermission[]
  windows?: { [key: string]: WindowConfig }
  entries: { [key: string]: ApplicationEntry }
  commands?: { [key: string]: ApplicationCommand }
  terminal?: { [commandName: string]: Omit<TerminalCommand, 'name' | 'applicationId'> }

  onReady?(app: IApplicationController): void | Promise<void>

  onLaunch?(app: IApplicationController): void | Promise<void>

  onRestore?(app: IApplicationController): void | Promise<void>

  onClose?(app: IApplicationController): void | Promise<void>
}

interface ApplicationConfigProvide {
  name: string
  entry: string
  command?: string
}

type IApplicationMeta = { [key: string]: any }

interface IApplicationController {
  id: string
  config: ApplicationConfig

  get meta(): { [key: string]: any }

  store: Pinia
  windows: Map<string, IWindowController>
  openWindowCount: ShallowRef<number>

  isRunning: boolean

  setRunning(value: boolean): void

  initApplication(): Promise<void>

  restoreApplication(): Promise<boolean>

  getWindowsByModel(model: string): IWindowController[]

  getFirstWindowByModel(model: string): IWindowController | undefined

  openWindow(
    model: string,
    windowStoredState?: WindowStoredState,
    options?: {
      isRestoring?: boolean
    },
  )

  closeWindow(windowId: string): void

  closeAllWindows(): void

  execCommand(command: string): Promise<CommandOutput | void>

  // deprecated
  get windowsOpened(): Reactive<Map<string, IWindowController>>
}

export interface IWindowController {
  application: IApplicationController
  instanced: boolean
  model: string

  config: WindowConfig
  override: WindowOverride

  get state(): WindowState

  // common
  get title(): string

  get windowTitle(): string

  get icon(): string | undefined

  // sizes
  get size(): WindowSize

  get position(): WindowPosition | undefined

  // minimize
  get isMinimizable(): boolean

  // maximize
  get isMaximizable(): boolean

  get isMaximized(): boolean

  // destroy
  get isDestroyable(): boolean

  // draggable
  get isDraggable(): boolean

  // resizable
  get isResizable(): boolean

  get actions(): {
    // position
    setActive(value: boolean)
    setFocus(value: boolean)
    bringToFront()
    setPosition(data: WindowPosition)

    // size
    setSize(data: WindowSize)

    // minimize
    minimize(): boolean

    // maximize
    toggleMaximize(): boolean
    maximize(): boolean
    unmaximize(): boolean

    // layout (snap / tiled zones)
    setLayout(layout: WindowLayout): boolean
    clearLayout(): boolean
    saveBoundsBeforeLayout(): void

    // destroy
    destroy(): boolean

    // workspace
    setWorkspace(workspaceId: string)

    // override
    setTitleOverride(title: string | undefined): void
    resetTitleOverride(): void
    setWindowTitleOverride(title: string | undefined): void
    resetWindowTitleOverride(): void
  }

  /** Payload restored with window state (e.g. explorer cwd in `path`). */
  get meta(): { path?: string } & Record<string, unknown>

  /** Chrome menu model ({@link DesktopMenuItem}[]; Menubar-compatible). */
  menu: import('../runtime/types/desktopMenu').DesktopMenuItem[]

  setMenu(menu: unknown[]): void

  destroy(): boolean

  /** Set by module-fs when an explorer surface mounts on this window. */
  fsExplorer?: any
}

export interface WindowContent {
  padded?: boolean
  centered?: boolean
}

export interface WindowConfig {
  title?: string
  category?: string
  icon?: string

  component?: Raw<Component>

  // position
  position?: WindowPosition

  // sizes
  size: WindowSize

  // minimize
  minimizable?: boolean

  // maximize
  maximized?: boolean
  maximizable?: boolean

  // destroy
  destroyable?: boolean

  // draggable
  draggable?: boolean

  // draggable
  resizable?: boolean

  overridable?: Partial<
    Record<
      | 'position'
      | 'size'
      | 'maximized'
      | 'draggable'
      | 'resizable'
      | 'destroyable'
      | 'minimizable',
      boolean
    >
  >
}

interface WindowStoredState {
  model: string
  state: WindowState
  meta: any
}

interface WindowOverride {
  title?: undefined | string
  windowTitle?: undefined | string
  icon?: undefined | string
}

interface WindowState {
  id: string
  createdAt: number

  category?: string
  workspace: string

  // position
  position?: WindowPosition

  // sizes
  size?: WindowSize

  // focused
  focused: boolean

  // minimize
  active?: boolean
  minimizable?: boolean

  // maximize
  maximized?: boolean
  maximizable?: boolean

  // destroy
  destroyable?: boolean

  // draggable
  draggable?: boolean

  // draggable
  resizable?: boolean

  /** Snap zone layout; `normal` is unset or explicit restore. */
  layout?: WindowLayout

  /** Bounds saved before first snap so restore can revert geometry. */
  boundsBeforeLayout?: WindowBounds
}

export type WindowLayout =
  | 'normal'
  | 'maximize'
  | 'bottom-half'
  | 'left-half'
  | 'right-half'
  | 'top-left'
  | 'top-right'
  | 'bottom-left'
  | 'bottom-right'

export interface WindowBounds {
  x: number
  y: number
  width: number
  height: number
}

export interface DesktopWorkAreaRect {
  x: number
  y: number
  width: number
  height: number
}

interface WindowSize {
  width?: WindowSizeValue
  height?: WindowSizeValue
  minWidth?: WindowSizeValue
  minHeight?: WindowSizeValue
  maxWidth?: WindowSizeValue
  maxHeight?: WindowSizeValue
}

interface WindowPosition {
  x?: number
  y?: number
  z?: number
}

type WindowSizeValue = number | string | undefined

interface ApplicationEntry {
  title?: string
  icon?: string
  category?: string
  visibility?: ApplicationEntryVisibility
  command: string | any
}

interface ApplicationEntryWithInherited extends ApplicationEntry {
  application: IApplicationController
  visibility: ApplicationEntryVisibility
}

type ApplicationEntryVisibility = 'primary' | 'secondary' | 'hidden'

// DESKTOP

interface IDesktopManager {
  defaultApps: import('vue').ComputedRef<Record<string, DefaultAppConfig>>

  getDefaultApp(feature: string): DefaultAppConfig | undefined

  setDefaultApp(
    feature: string,
    application: IApplicationController,
    entry: string,
  ): void
}

interface DocsModuleUserSourceConfig {
  cwd: string
  prefix?: string
  include?: string
  exclude?: string[]
}

interface DocsModuleConfig {
  basePath?: string
  title?: string
  sources?: DocsModuleUserSourceConfig[]
}

interface DesktopConfig {
  coreVersion?: string
  theme?: string
  modules?: string[]
  apps?: string[]

  docs?: DocsModuleConfig

  name?: string
  defaultApps?: DefaultAppsConfig
  features?: string[]

  systemBar?: DesktopSystemBarConfig
  dockBar?: DesktopDockBarConfig
  workspaces?: DesktopWorkspacesConfig
  windows?: DesktopWindowsConfig
}

interface DesktopWindowsConfig {
  position: 'relative' | 'absolute' | 'fixed'
}

interface DesktopDockBarConfig {
  enabled?: boolean
  position?: 'top' | 'bottom'
}

interface DesktopSystemBarConfig {
  enabled?: boolean
  position?: 'top' | 'bottom'
  startButton?: boolean
}

interface DesktopWorkspacesConfig {
  enabled?: boolean
}

// TERMINAL

type TerminalCommandArg = {
  name: string
  description?: string
  required?: boolean
}

type TerminalCommandOption = {
  flag: string
  description?: string
}

type TerminalCommand = {
  name: string
  applicationId: string
  description?: string
  usage?: string
  args?: TerminalCommandArg[]
  options?: TerminalCommandOption[]
}

interface ITerminalManager {
  addCommand(command: TerminalCommand): void
  listCommands(): string[]
  getCommand(name: string): TerminalCommand | undefined
  getAllCommands(): TerminalCommand[]
  execCommand(input: string): Promise<CommandOutput | void>
}

interface CommandOutput {
  message: string
  isError?: boolean
}

// DEFAULT APP

interface DefaultAppsConfig {
  terminal?: DefaultAppConfig
  browser?: DefaultAppConfig
  editor?: DefaultAppConfig

  [key: string]: DefaultAppConfig
}

  /** Stored default for a feature: which app and which entry key to use. */
  interface DefaultAppConfig {
    applicationId: string
    entry: string
  }

  module '*.vue' {
    import type { DefineComponent } from 'vue'
    const component: DefineComponent<{}, {}, any>
    export default component
  }
}

export function defineDesktopApp(config: ApplicationConfig)
export function defineDesktopConfig(config: DesktopConfig)
export function defineDesktopModule(
  definition: import('@nuxt/kit').NuxtModule<
    Record<string, unknown>,
    Record<string, unknown>,
    false
  > & { meta: { name: string; configKey: string } },
): ReturnType<typeof import('@nuxt/kit').defineNuxtModule>

export function defineDesktopTheme(
  definition: import('@nuxt/kit').NuxtModule<
    Record<string, unknown>,
    Record<string, unknown>,
    false
  >,
): ReturnType<typeof import('@nuxt/kit').defineNuxtModule>

export function setDesktopExtensionConfig(
  nuxt: Nuxt,
  configKey: string,
  resolved: Record<string, unknown>,
): void

export function mergeDesktopExtensionConfig(
  nuxt: Nuxt,
  configKey: string,
  partial: Record<string, unknown>,
): void

export function useDesktopConfig(): import('vue').ComputedRef<DesktopConfig>
export function useDesktopExtension<K extends string>(
  key: K,
): import('vue').ComputedRef<unknown>
export function hasDesktopModule(
  pkg: string,
): import('vue').ComputedRef<boolean>
export function hasDesktopApp(pkg: string): import('vue').ComputedRef<boolean>
export function hasDesktopExtension(
  key: string,
): import('vue').ComputedRef<boolean>

export {}
