import {
  Schema,
  SchemaEnum,
  SchemaReference,
  SchemaReferences,
  SchemaTypeName,
} from '@based/schema'
import { getPropType } from '@based/schema/dist/parse/utils.js'
import { Field, Fields } from './common.js'
import { measureTextWidth, Text } from '../components/Text/index.js'
import { Badge } from '../components/Badge/index.js'
import { prettyDate } from '@based/pretty-date'
import { Media } from '../components/Media/index.js'
import { UseInfiniteQueryOptions } from '../hooks/useInfiniteQuery.js'
import { Tooltip } from '../components/Tooltip/index.js'
import { Fragment } from 'react'

const ALWAYS_EXCLUDED_FIELDS = [
  'parents',
  'ancestors',
  'descendants',
  'aliases',
  'children',
  'type',
  'password',
]

function generateFinderProps(
  type: SchemaTypeName,
  schema: Schema,
): { fields: Fields } & Pick<
  UseInfiniteQueryOptions,
  'query' | 'totalQuery' | 'transformQueryResult'
> {
  return {
    fields: generateFields(type, schema),
    query: generateQuery(type, schema),
    totalQuery: generateTotalQuery(type, schema),
    transformQueryResult: (result: any) => result.data,
  }
}

function generateFields(type: SchemaTypeName, schema: Schema): Fields {
  const res: Fields = {}

  for (const [key, field] of Object.entries(schema.types[type].props)) {
    if (ALWAYS_EXCLUDED_FIELDS.includes(key)) continue

    const fieldType = getPropType(field)

    if (['object', 'binary'].includes(fieldType)) {
      continue
    }

    if (['id'].includes(key)) {
      continue
    }

    if (['multiline', 'html'].includes((field as any)?.format)) {
      continue
    }

    if (
      [
        'number',
        'int8',
        'uint8',
        'int16',
        'uint16',
        'int32',
        'uint32',
      ].includes(fieldType)
    ) {
      res[key] = {
        key,
        table: {
          title: key,
          width: Math.max(
            measureTextWidth('0000000', 'display-mono'),
            measureTextWidth(key, 'display-bold'),
          ),
        },
        renderAccessorFn: (item) => item[key],
        render: (content) => (
          <Text singleLine color="neutral80" variant="display-mono">
            {content}
          </Text>
        ),
      }
      continue
    }

    if (fieldType === 'boolean') {
      res[key] = {
        key,
        table: {
          title: key,
          width: Math.max(
            measureTextWidth('false', 'subtext-mono') + 2 * 6,
            measureTextWidth(key, 'display-bold'),
          ),
        },
        renderAccessorFn: (item) => item[key],
        render: (content) => (
          <Badge textVariant="mono" color="neutral-subtle">
            {content ? 'true' : 'false'}
          </Badge>
        ),
      }
      continue
    }

    if (fieldType === 'set') {
      res[key] = {
        key,
        table: {
          title: key,
          width: 150,
        },
        renderAccessorFn: (item) => item[key],
        render: (content) => {
          const setItems = content
          if (!setItems?.length) return null

          return (
            <Tooltip value={setItems.join(', ')}>
              <div
                style={{
                  width: '100%',
                  display: 'flex',
                  gap: 4,
                  overflow: 'hidden',
                }}
              >
                <div
                  style={{
                    overflow: 'hidden',
                  }}
                >
                  <Badge color="neutral-subtle" textVariant="mono">
                    {content[0]}
                  </Badge>
                </div>
                {setItems.length > 1 && (
                  <div style={{ flexShrink: 0 }}>
                    <Badge color="neutral-subtle" textVariant="mono">
                      +{setItems.length - 1}
                    </Badge>
                  </div>
                )}
              </div>
            </Tooltip>
          )
        },
      }
      continue
    }

    if (fieldType === 'enum') {
      const enumField = field as SchemaEnum
      const longestPossibleEnum = enumField.enum
        .map((e) => e.toString())
        .sort((a, b) => b.length - a.length)[0]
      const longestPossibleEnumWidth =
        measureTextWidth(longestPossibleEnum, 'subtext-mono') + 2 * 6

      res[key] = {
        key,
        table: {
          title: key,
          width: Math.max(
            longestPossibleEnumWidth,
            measureTextWidth(key, 'display-bold'),
          ),
        },
        renderAccessorFn: (item) => item[key],
        render: (content) =>
          content ? (
            <Badge textVariant="mono" color="neutral-subtle">
              {content}
            </Badge>
          ) : null,
      }
      continue
    }

    if (fieldType === 'reference') {
      const referenceField = field as SchemaReference
      const referenceProps =
        schema.types[referenceField.ref as SchemaTypeName].props

      if (Object.keys(referenceProps).includes('src')) {
        res[key] = {
          key,
          table: {
            width: 24,
          },
          grid: {
            type: 'media',
          },
          renderAccessorFn: (item) => item[key],
          render: (content) => {
            return (
              <div style={{ height: 24, width: 24, display: 'flex' }}>
                <Media data={content} />
              </div>
            )
          },
        }
        continue
      }

      res[key] = {
        key,
        table: {
          title: key,
          width: Math.max(
            measureTextWidth('re9999', 'subtext-mono') + 2 * 6,
            measureTextWidth(key, 'display-bold'),
          ),
        },
        renderAccessorFn: (item) => item[key]?.id,
        render: (content) => {
          if (!content) return null

          return (
            <Badge textVariant="mono" color="neutral-subtle">
              {content}
            </Badge>
          )
        },
      }
      continue
    }

    if (fieldType === 'references') {
      const referencesField = field as SchemaReferences
      const referencesProps =
        schema.types[referencesField.items.ref as SchemaTypeName].props

      if (
        Object.keys(referencesProps).some((e) => ['name', 'title'].includes(e))
      ) {
        res[key] = {
          key,
          table: {
            title: key,
            width: 200,
          },
          renderAccessorFn: (item) => item[key],
          render: (content, view) => {
            const text = content
              ?.map((e: any) => e['title'] || e['name'])
              .join(', ')

            if (!text) return null

            return (
              <Tooltip value={text}>
                <Text singleLine color="neutral80">
                  {text}
                </Text>
              </Tooltip>
            )
          },
        }
        continue
      }

      res[key] = {
        key,
        table: {
          title: key,
          width: Math.max(
            measureTextWidth('re9999', 'subtext-mono') +
              2 * 6 +
              +4 +
              measureTextWidth('+1', 'subtext-mono') +
              2 * 6,
            measureTextWidth(key, 'display-bold'),
          ),
        },
        renderAccessorFn: (item) => item[key],
        render: (content) => {
          const referredItems = content
          if (!referredItems?.length) return null
          const Wrapper = referredItems.length > 1 ? Tooltip : Fragment
          const wrapperProps =
            referredItems.length > 1
              ? { value: content.map((e: any) => e.id).join(', ') }
              : {}

          return (
            <Wrapper {...wrapperProps}>
              <div
                style={{
                  width: '100%',
                  display: 'flex',
                  gap: 4,
                  overflow: 'hidden',
                }}
              >
                <Badge color="neutral-subtle" textVariant="mono">
                  {content[0].id}
                </Badge>
                {referredItems.length > 1 && (
                  <Badge color="neutral-subtle" textVariant="mono">
                    +{referredItems.length - 1}
                  </Badge>
                )}
              </div>
            </Wrapper>
          )
        },
      }
      continue
    }

    if (fieldType === 'timestamp') {
      res[key] = {
        key,
        table: {
          title: key,
          width: Math.max(
            measureTextWidth('hh:mm dd/MM/yyyy', 'display-mono'),
            measureTextWidth(key, 'display-bold'),
          ),
        },
        grid: {
          type: 'description',
        },
        renderAccessorFn: (item) => item[key],
        render: (content, view) =>
          content ? (
            <Text
              singleLine
              variant="display-mono"
              color={view === 'grid' ? 'neutral60' : 'neutral80'}
            >
              {prettyDate(content, 'date-time')}
            </Text>
          ) : null,
      }
      continue
    }

    // if (key === 'id') {
    //   res[key] = {
    //     key,
    //     table: {
    //       title: 'id',
    //       width: measureTextWidth('fi9999', 'subtext-mono') + 2 * 6,
    //     },
    //     renderAccessorFn: (item) => item.id,
    //     render: (content) => (
    //       <Badge textVariant="mono" color="neutral-subtle">
    //         {content}
    //       </Badge>
    //     ),
    //   }
    //   continue
    // }

    if (key === 'src') {
      res[key] = {
        key,
        table: {
          width: 24,
        },
        grid: {
          type: 'media',
        },
        renderAccessorFn: (item) => item['src'],
        render: (content) => {
          return (
            <div style={{ height: 24, width: 24, display: 'flex' }}>
              <Media data={{ src: content }} />
            </div>
          )
        },
      }
      continue
    }

    if (['title', 'name'].includes(key)) {
      res[key] = {
        key,
        table: {
          title: key,
        },
        calendar: {
          type: 'title',
        },
        grid: {
          type: 'title',
        },
      }
      continue
    }

    res[key] = {
      key,
      table: {
        title: key,
        width: 200,
      },
    }
  }

  return Object.fromEntries(
    Object.entries(res).sort((a, b) => {
      if (a[1].grid?.type === 'media') {
        return -3
      }

      if (['title', 'name'].includes(a[0])) {
        return -2
      }

      if (a[1].calendar) {
        return 1
      }

      return 0
    }),
  )
}

function generateQuery(
  type: string,
  _schema: Schema,
): UseInfiniteQueryOptions['query'] {
  return ({ view, offset, limit, start, end, sort, filter }) => ({
    ...(view === 'calendar'
      ? {
          start,
          end,
          type,
        }
      : {
          offset,
          limit,
          type,
          sort,
          filter,
        }),
  })
}

function generateTotalQuery(
  type: string,
  _schema: Schema,
): UseInfiniteQueryOptions['totalQuery'] {
  return () => ({ type })
}

export { generateFinderProps }
