import { TemplateResult } from 'lit'
import { IconType } from '../../md/icons/Icons.js'
import { SplitLayout } from './SplitLayout.js'
import { SplitPanel } from './SplitPanel.js'

export type ItemRenderCallback = (item: SplitItem, visible: boolean) => TemplateResult

export interface ISplitItem {
  /**
   * The kind of opened item. This is to be used by the application
   * to recognize a kind of item to render. It is transparent to the layout system.
   */
  kind: string
  /**
   * The key of the opened item. This is to be used by the application to identify
   * the rendered item. It is also used by the manager to identify the item.
   * It has to be unique key across the layout.
   *
   * Note, multiple panels can render the same item, however, they all share
   * the same item's state.
   */
  key: string
  /**
   * Optional parent information that helps locating this object.
   * This is the item's parent, not related to a layout.
   */
  parent?: string
  /**
   * The label to render in the layout tab.
   */
  label: string
  /**
   * Whether the tab is pinned (cannot be closed or moved).
   */
  pinned?: boolean
  /**
   * The tab index.
   */
  index?: number
  /**
   * The icon defined in the internal library to render with the tab.
   */
  icon?: IconType
  /**
   * A tab that is always present in the layout. The user can't close this tab.
   */
  persistent?: boolean
  /**
   * A property to be used by the screen to indicate the property is being loaded
   * (from a data store, file, etc).
   */
  loading?: boolean
  /**
   * Indicates the item has been changed and is out of sync with the data store.
   */
  isDirty?: boolean

  /**
   * The layout value. Can be used to pass the object to render to the layout's render function.
   * This must be a serializable object as it will be passed to the `storeCallback`.
   *
   * This also can be used to temporary store unsaved changes to the object and sync the changes
   * on save action.
   */
  value?: unknown
}

export class SplitItem implements ISplitItem {
  kind = ''

  key = ''

  parent?: string

  label = ''

  pinned?: boolean

  index = 0

  icon?: IconType

  persistent?: boolean

  loading?: boolean

  isDirty?: boolean

  value?: unknown

  /**
   * @param manager A reference to the manager.
   * @param id The id of the item in the layout system. This value is never serialized.
   * @param schema A schema to restore the state of the panel.
   */
  constructor(
    public manager: SplitLayout,
    schema: ISplitItem
  ) {
    this.new(schema)
  }

  new(schema: ISplitItem): void {
    this.kind = schema.kind
    this.key = schema.key
    this.label = schema.label
    if (schema.parent) {
      this.parent = schema.parent
    } else {
      this.parent = undefined
    }
    if (schema.icon) {
      this.icon = schema.icon
    } else {
      this.icon = undefined
    }
    if (typeof schema.pinned === 'boolean') {
      this.pinned = schema.pinned
    } else {
      this.pinned = undefined
    }
    if (typeof schema.persistent === 'boolean') {
      this.persistent = schema.persistent
    } else {
      this.persistent = undefined
    }
    if (typeof schema.loading === 'boolean') {
      this.loading = schema.loading
    } else {
      this.loading = undefined
    }
    if (typeof schema.isDirty === 'boolean') {
      this.isDirty = schema.isDirty
    } else {
      this.isDirty = undefined
    }
    if (typeof schema.index === 'number') {
      this.index = schema.index
    } else {
      this.index = 0
    }
    this.value = schema.value
  }

  toJSON(): ISplitItem {
    const result: ISplitItem = {
      key: this.key,
      kind: this.kind,
      label: this.label,
    }
    if (this.parent) {
      result.parent = this.parent
    }
    if (this.icon) {
      result.icon = this.icon
    }
    if (typeof this.pinned === 'boolean') {
      result.pinned = this.pinned
    }
    if (typeof this.persistent === 'boolean') {
      result.persistent = this.persistent
    }
    if (typeof this.loading === 'boolean') {
      result.loading = this.loading
    }
    if (typeof this.isDirty === 'boolean') {
      result.isDirty = this.isDirty
    }
    if (typeof this.index === 'number') {
      result.index = this.index
    }
    if (this.value) {
      // try toJSON if any
      const typed = this.value as { toJSON: () => unknown }
      if (typeof typed.toJSON === 'function') {
        result.value = typed.toJSON()
      } else {
        result.value = this.value
      }
    }
    return result
  }

  // /**
  //  * @returns The parent of the item. Returns undefined when the item was removed from layout.
  //  * @deprecated Use `getParents()` instead.
  //  */
  // getParent(): SplitPanel | undefined {
  //   return this.manager.getParent(this.key);
  // }

  /**
   * @returns The parent of the item. Returns undefined when the item was removed from layout.
   */
  getParents(): SplitPanel[] {
    return this.manager.getParents(this.key)
  }

  // /**
  //  * Removes self from the layout. This removes the item from all panels.
  //  */
  // remove(): void {
  //   this.manager.removeItem(this.key);
  // }

  // /**
  //  * Sets the item active in the parent layout.
  //  */
  // setSelected(): void {
  //   const parent = this.getParent();
  //   if (parent) {
  //     parent.selected = this.key;
  //     this.notifyChange();
  //   }
  // }

  /**
   * Updates the label and triggers side effects like layout and view update.
   *
   * @param value The new value of the label. Empty values are ignored.
   * @param trigger Whether to trigger side effects (requesting a view update and notifying change)
   */
  setLabel(value: string, trigger = true): void {
    if (!value || this.label === value) {
      return
    }
    this.label = value
    if (trigger) {
      this.updateView()
      this.notifyChange()
    }
  }

  updateView(): void {
    const panels = this.getParents()
    panels.forEach((panel) => panel.updateView())
  }

  notifyChange(): void {
    this.manager.notifyChange()
  }
}
