import {StackCompactIcon, StackIcon} from '@sanity/icons'
import {type SchemaType} from '@sanity/types'
import {startCase} from 'lodash'

import {structureLocaleNamespace} from '../i18n'
import {type DocumentListBuilder} from './DocumentList'
import {DocumentTypeListBuilder, type DocumentTypeListInput} from './DocumentTypeList'
import {defaultIntentChecker} from './Intent'
import {type List} from './List'
import {type ListItem, ListItemBuilder} from './ListItem'
import {getOrderingMenuItemsForSchemaType, MenuItemBuilder} from './MenuItem'
import {DEFAULT_SELECTED_ORDERING_OPTION} from './Sort'
import {type Collection} from './StructureNodes'
import {type StructureContext} from './types'

const BUNDLED_DOC_TYPES = ['sanity.imageAsset', 'sanity.fileAsset']

function isBundledDocType(typeName: string) {
  return BUNDLED_DOC_TYPES.includes(typeName)
}

function isDocumentType(schemaType: SchemaType) {
  return schemaType.type?.name === 'document'
}

function isList(collection: Collection): collection is List {
  return collection.type === 'list'
}

export function getDocumentTypes({schema}: StructureContext): string[] {
  return schema
    .getTypeNames()
    .filter((n) => {
      const schemaType = schema.get(n)
      return schemaType && isDocumentType(schemaType)
    })
    .filter((n) => !isBundledDocType(n))
}

export function getDocumentTypeListItems(context: StructureContext): ListItemBuilder[] {
  const types = getDocumentTypes(context)
  return types.map((typeName) => getDocumentTypeListItem(context, typeName))
}

export function getDocumentTypeListItem(
  context: StructureContext,
  typeName: string,
): ListItemBuilder {
  const {schema} = context

  const type = schema.get(typeName)
  if (!type) {
    throw new Error(`Schema type with name "${typeName}" not found`)
  }

  const title = type.title || startCase(typeName)

  return new ListItemBuilder(context)
    .id(typeName)
    .title(title)
    .schemaType(type)
    .child((id, childContext) => {
      const parent = childContext.parent as Collection
      const parentItem = isList(parent)
        ? (parent.items.find((item) => item.id === id) as ListItem)
        : null

      let list = getDocumentTypeList(context, typeName)
      if (parentItem && parentItem.title) {
        list = list.title(parentItem.title)
      }

      return list
    })
}

export function getDocumentTypeList(
  context: StructureContext,
  typeNameOrSpec: string | DocumentTypeListInput,
): DocumentListBuilder {
  const {schema, resolveDocumentNode} = context

  const schemaType = typeof typeNameOrSpec === 'string' ? typeNameOrSpec : typeNameOrSpec.schemaType
  const typeName = typeof schemaType === 'string' ? schemaType : schemaType.name
  const spec: DocumentTypeListInput =
    typeof typeNameOrSpec === 'string' ? {schemaType} : typeNameOrSpec

  const type = schema.get(typeName)
  if (!type) {
    throw new Error(`Schema type with name "${typeName}" not found`)
  }

  const title = type.title || startCase(typeName)

  return new DocumentTypeListBuilder(context)
    .id(spec.id || typeName)
    .title(spec.title || title)
    .filter('_type == $type')
    .params({type: typeName})
    .schemaType(type)
    .defaultOrdering(DEFAULT_SELECTED_ORDERING_OPTION.by)
    .menuItemGroups(
      spec.menuItemGroups || [
        {
          id: 'sorting',
          title: 'Sort',
          i18n: {title: {key: 'menu-item-groups.actions-group', ns: structureLocaleNamespace}},
        },
        {
          id: 'layout',
          title: 'Layout',
          i18n: {title: {key: 'menu-item-groups.layout-group', ns: structureLocaleNamespace}},
        },
        {
          id: 'actions',
          title: 'Actions',
          i18n: {title: {key: 'menu-item-groups.sorting-group', ns: structureLocaleNamespace}},
        },
      ],
    )
    .child(
      spec.child ||
        ((documentId: string) => resolveDocumentNode({schemaType: typeName, documentId})),
    )
    .canHandleIntent(spec.canHandleIntent || defaultIntentChecker)
    .menuItems(
      spec.menuItems || [
        // Create new (from action button) will be added in serialization step of GenericList

        // Sort by <Y>
        ...getOrderingMenuItemsForSchemaType(context, type),

        // Display as <Z>
        new MenuItemBuilder(context)
          .group('layout')
          .i18n({title: {key: 'menu-items.layout.compact-view', ns: structureLocaleNamespace}})
          .title('Compact view') // fallback title
          .icon(StackCompactIcon)
          .action('setLayout')
          .params({layout: 'default'}),

        new MenuItemBuilder(context)
          .group('layout')
          .i18n({title: {key: 'menu-items.layout.detailed-view', ns: structureLocaleNamespace}})
          .title('Detailed view') // fallback title
          .icon(StackIcon)
          .action('setLayout')
          .params({layout: 'detail'}),

        // Create new (from menu) will be added in serialization step of GenericList
      ],
    )
}
