// @unocss-include
import { FactorObject, objectId, toLabel, vue } from '@factor/api'
import type { InputOption } from '@factor/ui'
import type { StandardUserConfig, TableCardConfig } from './tables'
import type { Theme } from './theme'

export type EditorState = {
  stateCardId: string
  statePageId: string
  stateRegionKey: 'header' | 'page' | 'footer'
  storeCardOrder: string[]
}

// need to generalize the case, currently can't figure out how to recreate
// this constructor that vue doesn't export (but uses)
export interface ComponentConstructor {
  new (...args: any[]): vue.ComponentPublicInstance<{ card: Card }>
}

interface CardRender<T extends ComponentConstructor = ComponentConstructor> {
  el?: vue.Component
  props?: Partial<InstanceType<T>['$props']>
  options: InputOption[]
}

interface CardTemplateSettings<U extends string = string, T extends ComponentConstructor = ComponentConstructor> {
  templateKey: U
  title?: string
  description?: string
  category?: string[]
  icon?: string
  iconStyle?: string
  thumb?: string
  defaultConfig?: Record<string, unknown>
  el: T
  render?: (card: Card) => vue.ComputedRef<CardRender<ComponentConstructor>>
  isContainer?: boolean
  isRegion?: boolean
  options?: InputOption[]
}

export class CardTemplate<U extends string = string, T extends ComponentConstructor = ComponentConstructor> extends FactorObject<
CardTemplateSettings
> {
  constructor(settings: CardTemplateSettings<U, T>) {
    super('CardTemplate', { title: toLabel(settings.templateKey), ...settings })
  }
}

export type CardSettings<T extends StandardUserConfig = StandardUserConfig > = TableCardConfig<T> & {
  theme?: Theme
}

export class Card<T extends StandardUserConfig = StandardUserConfig> extends FactorObject<CardSettings<T>> {
  cardId = this.settings.cardId || objectId({ prefix: 'card' })
  parentId = this.settings.parentId
  depth = vue.ref(this.settings.depth || 0)
  regionKey = this.settings.regionKey
  templateKey = vue.ref(this.settings.templateKey || (this.parentId ? 'area' : 'engine'))
  slug = vue.ref(this.settings.slug)
  title = vue.ref(this.settings.title)

  displayTitle = vue.computed(() => this.title.value || toLabel(this.slug.value))
  userConfig = vue.ref<T>(this.settings.userConfig || {} as T)
  cards = vue.shallowRef((this.settings.cards || []).map(c => this.initCard({ cardConfig: c })))

  tpl = vue.computed(() => {
    const templateKey = this.templateKey.value
    const cardTemplates = this.settings.theme?.cardTemplates
    const out = cardTemplates?.find(t => t.settings.templateKey === templateKey)
    return out
  })

  options: vue.ComputedRef<InputOption[]> = vue.computed(() => {
    const standardOptions = this.settings.theme?.cardOptions?.(this).value || []
    const tplOptions = this.tpl.value?.settings.options || []

    return [...tplOptions, ...standardOptions]
  })

  constructor(settings: CardSettings<T>) {
    super('Card', settings)
  }

  transformInput() {
    return {}
  }

  initCard(args: { cardConfig: TableCardConfig }) {
    const { cardConfig } = args
    const card = new Card({ parentId: this.cardId, ...cardConfig, depth: this.depth.value + 1, theme: this.settings.theme })
    return card
  }

  addCard(args: { cardConfig: TableCardConfig }) {
    const { cardConfig } = args
    const card = this.initCard({ cardConfig })
    this.cards.value = [card, ...this.cards.value]
  }

  update(cardConfig?: Partial<TableCardConfig<T>>) {
    if (!cardConfig)
      return
    const availableKeys = ['title', 'slug', 'userConfig', 'templateKey']
    const entries = Object.entries(cardConfig).filter(([key]) => availableKeys.includes(key))
    entries.forEach(([key, value]) => {
      if (value !== undefined && vue.isRef(this[key as keyof this]))
        (this[key as keyof this] as vue.Ref).value = value

      this.settings = { ...this.settings, [key as keyof T]: value }
    })
  }

  toConfig(): TableCardConfig<T> {
    const { theme: _, ...rest } = this.settings

    const cards = this.cards.value.map(c => c.toConfig())
    return {
      templateKey: this.templateKey.value,
      cardId: this.cardId,
      ...rest,
      title: this.title.value,
      slug: this.slug.value,
      userConfig: this.userConfig.value as T,
      cards,
    }
  }
}
