import { useLayoutEffect, useMemo, useRef, useState } from 'react'
import { colors } from '../../utils/colors.js'
import { radius } from '../../utils/radius.js'
import { styled } from 'inlines'
import {
  useInfiniteQuery,
  UseInfiniteQueryOptions,
} from '../../hooks/useInfiniteQuery.js'
import { Field, Fields, ItemActions, Select } from '../../utils/common.js'
import { CheckboxInput } from '../CheckboxInput/index.js'
import { Menu } from '../Menu/index.js'
import { IconButton } from '../IconButton/index.js'
import { Text } from '../Text/index.js'
import { Media } from '../Media/index.js'

function getFieldPriorityByType(field: Field) {
  if (field.grid.type === 'media') return 0
  if (field.grid.type === 'title') return 1
  return 2
}

type GridItemProps = {
  data: any
  fields: Fields
  onClick?: () => void
} & Pick<InternalGridProps, 'select' | 'onSelectChange' | 'itemActions'>

function GridItem({
  data,
  fields,
  select,
  onSelectChange,
  onClick,
  itemActions,
}: GridItemProps) {
  const selected = select?.includes(data.id)
  const [forceHover, setForceHover] = useState(false)

  console.log('data', data)

  return (
    <styled.div
      onClick={onClick}
      data-selected={selected ? true : undefined}
      data-forcehover={forceHover ? true : undefined}
      style={{
        position: 'relative',
        display: 'flex',
        flexDirection: 'column',
        padding: 8,
        borderRadius: radius[16],
        '&:hover, &[data-selected], &[data-forcehover]': {
          background: colors.neutral10Adjusted,
        },
        '&:hover > .selectContainer, &[data-selected] > .selectContainer, &[data-forcehover] > .selectContainer':
          {
            opacity: '100% !important',
          },
        '&:hover > .actionContainer, &[data-selected] > .actionContainer, &[data-forcehover] > .actionContainer':
          {
            opacity: '100% !important',
          },
      }}
    >
      {onSelectChange && (
        <div
          className="selectContainer"
          style={{
            position: 'absolute',
            top: 16,
            left: 16,
            opacity: 0,
            height: 32,
            width: 32,
            borderRadius: radius[8],
            background: colors.neutralInverted100,
            overflow: 'hidden',
          }}
          onClick={(e) => {
            e.stopPropagation()
          }}
        >
          <div
            style={{
              width: '100%',
              height: '100%',
              background: colors.neutral10Adjusted,
              display: 'flex',
              justifyContent: 'center',
              alignItems: 'center',
            }}
          >
            <CheckboxInput
              value={selected}
              onChange={() => {
                if (!select) {
                  onSelectChange([data.id])
                  return
                }

                if (select.includes(data.id)) {
                  onSelectChange(select.filter((e) => e !== data.id))
                  return
                }

                onSelectChange([...select, data.id])
              }}
            />
          </div>
        </div>
      )}
      {itemActions && (
        <div
          className="actionContainer"
          style={{
            position: 'absolute',
            top: 16,
            right: 16,
            opacity: 0,
            height: 32,
            width: 32,
            borderRadius: radius[8],
            background: colors.neutralInverted100,
            overflow: 'hidden',
          }}
          onClick={(e) => {
            e.stopPropagation()
          }}
        >
          <Menu
            onOpenChange={(open) => {
              setForceHover(open ? true : false)
            }}
          >
            <Menu.Trigger>
              {({ open }) => (
                <div
                  onClick={(e) => {
                    e.stopPropagation()
                  }}
                  style={{
                    height: '100%',
                    width: '100%',
                    display: 'flex',
                    justifyContent: 'center',
                    alignItems: 'center',
                    background: colors.neutral10Adjusted,
                  }}
                >
                  <IconButton
                    forceHover={open}
                    icon="more-vertical"
                    size="small"
                  />
                </div>
              )}
            </Menu.Trigger>
            <Menu.Items>
              {itemActions.map((action) => (
                <Menu.Item
                  key={action.label}
                  leadIcon={action.icon}
                  onClick={() => {
                    action?.onClick(data)
                  }}
                  color={action.color}
                >
                  {action.label}
                </Menu.Item>
              ))}
            </Menu.Items>
          </Menu>
        </div>
      )}
      {Object.values(fields)
        .sort((a, b) => getFieldPriorityByType(a) - getFieldPriorityByType(b))
        .map((field, index) => (
          <div
            key={index}
            style={{ display: 'flex', minHeight: 24, alignItems: 'center' }}
          >
            {field.grid.type === 'media' ? (
              <div
                style={{
                  width: '100%',
                  borderRadius: radius[8],
                  background: colors.neutral10Adjusted,
                  border: `1px solid ${colors.neutral20Adjusted}`,
                  aspectRatio: 1,
                  overflow: 'hidden',
                  display: 'flex',
                  justifyContent: 'center',
                  alignItems: 'center',
                  marginBottom: 4,
                }}
              >
                <Media
                  data={
                    field.renderAccessorFn ? field.renderAccessorFn(data) : data
                  }
                />
              </div>
            ) : field.render ? (
              <>
                {field.render(
                  field.renderAccessorFn ? field.renderAccessorFn(data) : data,
                  'grid',
                )}
              </>
            ) : field.grid.type === 'title' ? (
              <Text singleLine variant="display-medium" color="neutral100">
                {data[field.key]}
              </Text>
            ) : (
              <Text singleLine variant="display-medium" color="neutral60">
                {data[field.key]}
              </Text>
            )}
          </div>
        ))}
    </styled.div>
  )
}

type GridColumnWidth = 'regular' | 'large'

type InternalGridProps = {
  data: any[]
  fields: Fields
  virtualized?: boolean
  totalCount?: number
  onScroll?: (
    firstVisibleItemIndex: number,
    lastVisibleItemIndex: number,
    realFirstVisibleItemIndex: number,
    realLastVisibleItemIndex: number,
  ) => void
  select?: Select
  onSelectChange?: (select?: Select) => void
  onItemClick?: (item: any) => void
  itemActions?: ItemActions
  columnWidth?: GridColumnWidth
}

const GRID_GAP = 4
const GRID_X_PADDING = 12
const GRID_REGULAR_MIN_COLUMN_WIDTH = 180
const GRID_LARGE_MIN_COLUMN_WIDTH = 300

function InternalGrid({
  data,
  fields: fieldsProp,
  virtualized,
  totalCount,
  onScroll,
  select,
  onSelectChange,
  onItemClick,
  itemActions,
  columnWidth = 'regular',
}: InternalGridProps) {
  const fields = useMemo(
    () =>
      Object.fromEntries(
        Object.entries(fieldsProp).filter(([key, field]) =>
          Boolean(field.grid),
        ),
      ),
    [fieldsProp],
  )
  const total = totalCount ?? data.length
  const ref = useRef<HTMLDivElement>()
  const [columns, setColumns] = useState(0)
  const [width, setWidth] = useState(0)
  const [firstRow, setFirstRow] = useState(0)
  const height = useMemo(() => {
    const padding = 2 * 8
    const heightOfFields = (Object.values(fields).length - 1) * 24
    const heightOfImageField = width - padding + 4
    return padding + heightOfFields + heightOfImageField
  }, [width])

  useLayoutEffect(() => {
    if (!ref.current) return () => {}

    function handleScroll() {
      const { clientWidth, clientHeight, scrollTop } = ref.current
      const containerWidth = clientWidth - GRID_X_PADDING * 2
      const columns = Math.floor(
        containerWidth /
          ((columnWidth === 'large'
            ? GRID_LARGE_MIN_COLUMN_WIDTH
            : GRID_REGULAR_MIN_COLUMN_WIDTH) +
            GRID_GAP),
      )
      const width = Math.floor(
        (containerWidth - (columns - 1) * GRID_GAP) / columns,
      )
      const rows = Math.ceil(clientHeight / (height + GRID_GAP))
      const extra = columns
      const firstRow = Math.floor(scrollTop / (height + GRID_GAP))
      const firstRowWithOverscan = Math.max(0, firstRow - extra)
      const lastRow = firstRow + rows
      const lastRowWithOverscan = lastRow + extra

      setColumns(columns)
      setWidth(width)
      setFirstRow(firstRowWithOverscan)

      onScroll?.(
        firstRowWithOverscan * columns,
        lastRowWithOverscan * columns,
        firstRow * columns,
        lastRow * columns,
      )
    }

    handleScroll()
    ref.current.addEventListener('scroll', handleScroll)
    const resizeObserver = new ResizeObserver(handleScroll)
    resizeObserver.observe(ref.current)

    return () => {
      ref.current.removeEventListener('scroll', handleScroll)
      resizeObserver.disconnect()
    }
  }, [ref, height, onScroll, columnWidth])

  return (
    <div
      style={{
        width: '100%',
        height: '100%',
        overflowY: 'auto',
        display: 'grid',
        gap: GRID_GAP,
        gridTemplateColumns: `repeat(${columns}, minmax(0, ${width}px))`,
        padding: '8px 12px',
      }}
      ref={ref}
    >
      {virtualized &&
        Array.from({ length: columns }).map((_, i) => (
          <div
            key={'above' + i}
            style={{
              height: firstRow * (height + GRID_GAP),
            }}
          />
        ))}
      {data.map((item) => (
        <GridItem
          key={item.id}
          data={item}
          fields={fields}
          select={select}
          onSelectChange={onSelectChange}
          onClick={() => {
            onItemClick?.(item)
          }}
          itemActions={itemActions}
        />
      ))}
      {Array.from({
        length: (columns - (data.length % columns)) % columns,
      }).map((_, i) => (
        <div key={`padding` + i} />
      ))}
      {virtualized &&
        Array.from({ length: columns }).map((_, i) => (
          <div
            key={'below' + i}
            style={{
              height:
                ((total - firstRow * columns - data.length) / columns) *
                (height + GRID_GAP),
            }}
          />
        ))}
    </div>
  )
}

type GridProps = Pick<
  InternalGridProps,
  | 'data'
  | 'fields'
  | 'select'
  | 'onSelectChange'
  | 'itemActions'
  | 'onItemClick'
>

function Grid(props: GridProps) {
  return <InternalGrid {...props} />
}

type VirtualizedGridProps = Pick<
  InternalGridProps,
  | 'data'
  | 'fields'
  | 'select'
  | 'onSelectChange'
  | 'itemActions'
  | 'onItemClick'
>

function VirtualizedGrid({ data, ...props }: GridProps) {
  const [scroll, setScroll] = useState({ first: 0, last: 0 })

  return (
    <InternalGrid
      virtualized
      data={data.slice(scroll.first, scroll.last)}
      totalCount={data.length}
      onScroll={(first, last) => {
        if (scroll.first !== first || scroll.last !== last) {
          setScroll({ first, last })
        }
      }}
      {...props}
    />
  )
}

type BasedGridProps = Omit<UseInfiniteQueryOptions, 'view'> &
  Pick<
    InternalGridProps,
    'fields' | 'select' | 'onSelectChange' | 'itemActions' | 'onItemClick'
  >

function BasedGrid({
  query,
  totalQuery,
  transformQueryResult,
  ...props
}: BasedGridProps) {
  const { data, total, handleScroll } = useInfiniteQuery({
    view: 'grid',
    query,
    totalQuery,
    transformQueryResult,
  })

  return (
    <InternalGrid
      data={data}
      totalCount={total}
      onScroll={handleScroll}
      virtualized
      {...props}
    />
  )
}

export { InternalGrid, Grid, VirtualizedGrid, BasedGrid }
export type {
  InternalGridProps,
  GridProps,
  VirtualizedGridProps,
  BasedGridProps,
  GridColumnWidth,
}
