import { useApplicationManager } from '../../composables/useApplicationManager'
import { useDesktopWindowStore } from '../../stores/storeDesktopWindow'
import { deepClone } from '../../utils/utilCommon'
import { markRaw, reactive } from '@vue/reactivity'
import { defineAsyncComponent } from 'vue'

export class WindowController implements IWindowController {
  public readonly application: IApplicationController

  public readonly instanced: boolean = true
  public readonly model: string

  public config: WindowConfig = {
    title: '',
    category: '',

    component: undefined,

    // position
    position: {
      x: 0,
      y: 0,
      z: 0,
    },

    // sizes
    size: {
      width: undefined,
      height: undefined,
    },

    // minimize
    minimizable: true,

    // maximize
    maximized: false,
    maximizable: false,

    // destroy
    destroyable: true,

    // draggable
    draggable: true,

    // resizable
    resizable: true,
  }

  public override: WindowOverride = reactive({})

  private storedState: WindowStoredState

  constructor(
    application: IApplicationController,
    model: string,
    windowConfig: WindowConfig,
    windowStoredState: WindowStoredState,
  ) {
    this.application = application
    this.model = model

    this.storedState = windowStoredState

    this.setConfig(windowConfig)

    this.restoreState()
  }

  private setConfig(config: WindowConfig) {
    // component
    if (config.component) {
      this.config.component = markRaw(defineAsyncComponent(config.component))
    }

    // title
    if (config.title) this.config.title = config.title
    if (config.icon) this.config.icon = config.icon

    // position
    if (!this.config.position) this.config.position = { x: 0, y: 0, z: 0 }
    if (config.position?.x) this.config.position.x = config.position.x
    if (config.position?.y) this.config.position.y = config.position.y
    if (config.position?.z) this.config.position.z = config.position.z

    // sizes
    if (config.size?.width) this.config.size.width = config.size.width
    if (config.size?.height) this.config.size.height = config.size.height
    if (config.size?.minWidth) this.config.size.minWidth = config.size.minWidth
    if (config.size?.minHeight)
      this.config.size.minHeight = config.size.minHeight
    if (config.size?.maxWidth) this.config.size.maxWidth = config.size.maxWidth
    if (config.size?.maxHeight)
      this.config.size.maxHeight = config.size.maxHeight

    // minimize
    if (typeof config.minimizable !== 'undefined') {
      this.config.minimizable = config.minimizable
    }
    if (typeof config.maximizable !== 'undefined') {
      this.config.maximizable = config.maximizable
    }

    // maximize
    if (typeof config.maximized !== 'undefined') {
      this.config.maximized = config.maximized
    }

    // draggable
    if (typeof config.draggable !== 'undefined') {
      this.config.draggable = config.draggable
    }

    // resizable
    if (typeof config.resizable !== 'undefined') {
      this.config.resizable = config.resizable
    }

    // destroy
    if (typeof config.destroyable !== 'undefined') {
      this.config.destroyable = config.destroyable
    }

    const DEFAULT_OVERRIDABLE = {
      draggable: true,
      resizable: false,
      position: false,
      size: false,
      maximized: false,
      destroyable: false,
      minimizable: false,
      maximizable: false,
    }

    DEFAULT_OVERRIDABLE.position = DEFAULT_OVERRIDABLE.draggable
    DEFAULT_OVERRIDABLE.size = DEFAULT_OVERRIDABLE.resizable
    DEFAULT_OVERRIDABLE.maximized = DEFAULT_OVERRIDABLE.maximizable

    this.config.overridable = {
      ...DEFAULT_OVERRIDABLE,
      ...(config.overridable || {}),
    }
  }

  // state

  get state() {
    return this.storedState.state
  }

  private restoreState() {
    const overridable = this.config.overridable || {}

    for (const key in overridable) {
      if (overridable[key as keyof typeof overridable]) {
        const stateKey = key as keyof WindowState

        if (typeof this.state[stateKey] === 'undefined') {
          if (typeof this.config[stateKey] === 'object') {
            this.state[stateKey] = deepClone(this.config[stateKey])
          } else {
            this.state[stateKey] = !!this.config[stateKey]
          }
        }
      }
    }
  }

  private setState(state: WindowState) {
    this.storedState.state = state
  }

  // meta

  get meta() {
    return this.storedState.meta
  }

  // position

  public setPosition(data: { x: number; y: number }) {
    if (!this.state.position) {
      this.state.position = { x: 0, y: 0 }
    }

    this.state.position.x = data.x
    this.state.position.y = data.y
  }

  public setActive(value: boolean) {
    this.state.active = value
  }

  public setFocus(value: boolean) {
    this.state.focused = value
  }

  public focus() {
    const applicationManager = useApplicationManager()
    const desktopWindowStore = useDesktopWindowStore()

    if (!this.state.position) {
      this.state.position = { x: 0, y: 0, z: 0 }
    }

    for (const window of applicationManager.windowsOpened.value.values()) {
      if (window !== this) {
        window.setFocus(false)
      }
    }

    this.setFocus(true)
    this.state.position.z = desktopWindowStore.incrementPositionZ()
  }

  // common

  get title(): string {
    if (typeof this.override.title !== 'undefined') {
      return this.override.title
    }

    if (this.config.title) {
      return this.config.title
    }

    return this.application.config.title
  }

  get windowTitle(): string {
    if (typeof this.override.windowTitle !== 'undefined') {
      return this.override.windowTitle
    }

    return this.title
  }

  get icon() {
    if (typeof this.override.icon !== 'undefined') {
      return this.override.icon
    }

    if (this.config.icon) {
      return this.config.icon
    }

    return this.application.config.icon
  }

  // position

  get position() {
    if (typeof this.state.position === 'undefined') {
      return this.config.position
    }

    return this.state.position
  }

  // sizes

  get size() {
    const stateSize = this.state.size || {}
    const configSize = this.config.size || {}

    return {
      width:
        typeof stateSize.width !== 'undefined'
          ? stateSize.width
          : configSize.width,
      height:
        typeof stateSize.height !== 'undefined'
          ? stateSize.height
          : configSize.height,
      minWidth:
        typeof stateSize.minWidth !== 'undefined'
          ? stateSize.minWidth
          : configSize.minWidth,
      maxWidth:
        typeof stateSize.maxWidth !== 'undefined'
          ? stateSize.maxWidth
          : configSize.maxWidth,
      minHeight:
        typeof stateSize.minHeight !== 'undefined'
          ? stateSize.minHeight
          : configSize.minHeight,
      maxHeight:
        typeof stateSize.maxHeight !== 'undefined'
          ? stateSize.maxHeight
          : configSize.maxHeight,
    }
  }

  public setSize(data: { width: number; height: number }) {
    if (!this.state.size) {
      this.state.size = { width: undefined, height: undefined }
    }

    this.state.size.width = data.width
    this.state.size.height = data.height
  }

  // minimize

  get isMinimizable() {
    if (this.config.overridable?.minimizable) {
      return !!this.state.minimizable
    }

    return !!this.config.minimizable
  }

  public minimize() {
    if (!this.isMinimizable) {
      return false
    }

    this.state.active = false
    return true
  }

  public unminimize() {
    this.state.active = true
    return true
  }

  public toggleMinimize() {
    this.state.active = !this.state.active
    this.focus()
  }

  // maximize

  get isMaximizable() {
    if (typeof this.state.maximizable === 'undefined') {
      return !!this.config.maximizable
    }

    return this.state.maximizable
  }

  get isMaximized() {
    if (this.state.layout === 'maximize') {
      return true
    }

    if (this.config.overridable?.maximized) {
      return !!this.state.maximized
    }

    return !!this.config.maximized
  }

  private parseSizeValue(value: WindowSizeValue | undefined, fallback: number): number {
    if (typeof value === 'number') {
      return value
    }
    if (typeof value === 'string') {
      const parsed = Number.parseFloat(value)
      if (!Number.isNaN(parsed)) {
        return parsed
      }
    }
    return fallback
  }

  public saveBoundsBeforeLayout() {
    if (this.state.layout && this.state.layout !== 'normal') {
      return
    }

    const pos = this.position
    const size = this.size

    this.state.boundsBeforeLayout = {
      x: pos?.x ?? 0,
      y: pos?.y ?? 0,
      width: this.parseSizeValue(size.width, 400),
      height: this.parseSizeValue(size.height, 300),
    }
  }

  public setLayout(layout: WindowLayout) {
    if (layout === 'normal') {
      return this.clearLayout()
    }

    if (!this.isMaximizable) {
      return false
    }

    this.saveBoundsBeforeLayout()
    this.state.layout = layout
    this.state.maximized = layout === 'maximize'
    return true
  }

  public clearLayout() {
    const bounds = this.state.boundsBeforeLayout
    if (bounds) {
      this.setPosition({ x: bounds.x, y: bounds.y })
      this.setSize({ width: bounds.width, height: bounds.height })
      delete this.state.boundsBeforeLayout
    }

    this.state.layout = 'normal'
    this.state.maximized = false
    return true
  }

  public toggleMaximize() {
    if (!this.isMaximizable) {
      return false
    }

    if (this.isMaximized) {
      return this.clearLayout()
    }

    return this.setLayout('maximize')
  }

  public maximize() {
    if (!this.isMaximizable) {
      return false
    }

    return this.setLayout('maximize')
  }

  public unmaximize() {
    if (!this.isMaximizable) {
      return false
    }

    return this.clearLayout()
  }

  // destroy

  get isDestroyable() {
    if (this.config.overridable?.destroyable) {
      return !!this.state.destroyable
    }

    return !!this.config.destroyable
  }

  public destroy() {
    if (!this.isDestroyable) {
      return false
    }

    this.application.closeWindow(this.state.id)

    return true
  }

  // draggable
  get isDraggable() {
    if (this.config.overridable?.draggable) {
      return !!this.state.draggable
    }

    return !!this.config.draggable
  }

  // resizable
  get isResizable() {
    if (this.config.overridable?.resizable) {
      return !!this.state.resizable
    }

    return !!this.config.resizable
  }

  // workspace
  public setWorkspace(workspaceId: string) {
    this.state.workspace = workspaceId
  }

  // override

  public setTitleOverride(value: undefined | string) {
    this.override.title = value
  }

  public resetTitleOverride() {
    this.override.title = undefined
  }

  public setWindowTitleOverride(value: undefined | string) {
    this.override.windowTitle = value
  }

  public resetWindowTitleOverride() {
    this.override.windowTitle = undefined
  }

  // menu

  public menu: any[] = []

  public setMenu(menu: any[]) {
    this.menu = menu
  }

  // deprecated ?
  get actions() {
    return {
      // position
      setActive: this.setActive.bind(this),
      setFocus: this.setFocus.bind(this),
      bringToFront: this.focus.bind(this),
      focus: this.focus.bind(this),
      setPosition: this.setPosition.bind(this),

      // size
      setSize: this.setSize.bind(this),

      // minimize
      minimize: this.minimize.bind(this),
      toggleMinimize: this.toggleMinimize.bind(this),

      // maximize
      toggleMaximize: this.toggleMaximize.bind(this),
      maximize: this.maximize.bind(this),
      unmaximize: this.unmaximize.bind(this),

      // layout
      setLayout: this.setLayout.bind(this),
      clearLayout: this.clearLayout.bind(this),
      saveBoundsBeforeLayout: this.saveBoundsBeforeLayout.bind(this),

      // destroy
      destroy: this.destroy.bind(this),

      // workspace
      setWorkspace: this.setWorkspace.bind(this),

      // override
      setTitleOverride: this.setTitleOverride.bind(this),
      resetTitleOverride: this.resetTitleOverride.bind(this),
      setWindowTitleOverride: this.setWindowTitleOverride.bind(this),
      resetWindowTitleOverride: this.resetWindowTitleOverride.bind(this),
    }
  }
}
