import { ArrayField as ArrayFieldType, SchemaFieldProps, VevProps } from '@vev/utils';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { SilkeBox } from '../../silke-box';
import { SilkeButton } from '../../silke-button';
import { SilkeIcon } from '../../silke-icon';
import { SilkeList } from '../../silke-list';
import { SilkeText, SilkeTextSmall } from '../../silke-text';
import { ArrayFieldModal } from './array-field-modal';
import { SilkeOverflowMenu } from '../../silke-overflow-menu';
import { generateImage, generateTitle, getImageFieldUrl } from './utils';
import { isString, startCase } from 'lodash';
import { getTitle } from './utils/schema-util';
import { getSchemaDefaultValue } from '../schema-utils';
import type { SilkeListItemData } from '../../silke-list/types';

/**
 * Checks if `of` is a single field definition object (e.g. `{ type: 'string' }`)
 * as opposed to a string literal like `'string'` or an array of VevProps.
 */
function isSingleFieldOf(of: any): of is VevProps {
  return of && typeof of === 'object' && !Array.isArray(of) && 'type' in of;
}

/**
 * Normalizes `schema.of` — converts the single-field shorthand `{ type: 'string' }`
 * into the primitive string `'string'` for code paths that expect it, while preserving
 * the full field definition for modal rendering.
 */
function getNormalizedOf(schema: ArrayFieldType): VevProps[] | 'string' | 'number' | 'object' {
  if (isSingleFieldOf(schema.of)) {
    if (schema.of.type === 'string' || schema.of.type === 'number') {
      return schema.of.type;
    }
    return [schema.of];
  }
  return schema.of as any;
}

/** Resolves a list item icon: emoji icon for image URLs, silke icon name for fallback. */
function resolveItemIcon(
  item: any,
  fieldSchema: VevProps | VevProps[] | string | undefined,
): any {
  // Check preview/custom image first
  const imageUrl = generateImage(item, fieldSchema);
  if (imageUrl && isString(imageUrl)) {
    return { type: 2, value: { url: imageUrl } };
  }

  // Check if any sub-field is an image field
  if (typeof item === 'object' && item) {
    const fieldUrl = getImageFieldUrl(item, fieldSchema);
    if (isString(fieldUrl)) return { type: 2, value: { url: fieldUrl } };
    if (fieldUrl === true) return 'image';
  }

  return undefined;
}

/** Finds the field schema for a given array item (handles multi-type `of`). */
function getFieldSchema(
  item: any,
  schema: ArrayFieldType,
): { fieldSchema: VevProps | VevProps[] | string | undefined; schemaType: string | undefined } {
  const [schemaType] = typeof item === 'object' && item ? Object.keys(item) : [];
  const normalizedOf = getNormalizedOf(schema);
  const fieldSchema = Array.isArray(normalizedOf)
    ? normalizedOf.find((s: VevProps) => s.name === schemaType)
    : normalizedOf === 'object' && schema.preview
      ? schema
      : normalizedOf;
  return { fieldSchema, schemaType };
}

const ArrayField = ({
  value = [],
  schema,
  disabled,
  readonly,
  onChange,
}: SchemaFieldProps<ArrayFieldType>) => {
  const [selected, setSelected] = useState<number | undefined>(undefined);
  const [schemaIndex, setSchemaIndex] = useState(0);
  const headerRef = useRef<any>(null);
  const valueRef = useRef(value);

  useEffect(() => {
    valueRef.current = value;
  }, [value]);

  const addItem = useCallback(
    (defaultValue: any) => {
      const newValue = [...valueRef.current, defaultValue];
      valueRef.current = newValue;
      onChange(newValue);
      setSelected(newValue.length - 1);
    },
    [onChange],
  );

  const handleDeleteItem = useCallback(() => {
    const update = value.filter((_, i) => i !== selected);
    onChange(update);
    setSelected(undefined);
  }, [onChange, value, selected]);

  if (!Array.isArray(value)) value = [];

  const isMultiType = Array.isArray(schema.of) && !isSingleFieldOf(schema.of) && schema.of.length > 1;

  const listItems: SilkeListItemData[] = useMemo(
    () =>
      value.map((item, i) => {
        const { fieldSchema, schemaType } = getFieldSchema(item, schema);
        return {
          label: typeof item === 'string' ? item : generateTitle(item, fieldSchema) || '',
          icon: resolveItemIcon(item, fieldSchema),
          active: selected === i,
          children: <SilkeIcon icon="drag" data-drag-handle />,
          onClick: () => {
            if (isMultiType) {
              const schemaValues = (schema.of as VevProps[]).map((s) => s.name);
              setSchemaIndex(schemaValues.indexOf(schemaType!) || 0);
            }
            setSelected(i);
          },
        };
      }),
    [value, schema, selected, isMultiType],
  );

  const handleAddDefault = useCallback(() => {
    const normalizedOf = getNormalizedOf(schema);
    const defaultValue = Array.isArray(normalizedOf)
      ? getSchemaDefaultValue(normalizedOf[0])
      : normalizedOf === 'object' && 'fields' in schema
        ? getSchemaDefaultValue(schema.fields)
        : normalizedOf === 'number'
          ? 0
          : '';
    addItem(defaultValue);
  }, [schema, addItem]);

  return (
    <SilkeBox column flex>
      <SilkeBox flex ref={headerRef} vPad="s">
        <SilkeBox flex vAlign="center">
          <SilkeTextSmall weight="strong">{getTitle(schema)}</SilkeTextSmall>
        </SilkeBox>
        <SilkeBox align="end">
          {isMultiType ? (
            <SilkeOverflowMenu
              size="s"
              icon="add"
              disabled={disabled}
              items={(schema.of as VevProps[]).map((item, i) => ({
                label: item.title || startCase(item.name),
                onClick: () => {
                  setSchemaIndex(i);
                  addItem(getSchemaDefaultValue((schema.of as VevProps[])[i]));
                },
              }))}
            />
          ) : (
            <SilkeButton
              icon="add"
              kind="ghost"
              size="s"
              onClick={handleAddDefault}
            />
          )}
        </SilkeBox>
      </SilkeBox>
      {schema.description && <SilkeText color="neutral-80">{schema.description}</SilkeText>}
      {selected !== undefined && (
        <ArrayFieldModal
          anchor={headerRef}
          value={value}
          selected={selected}
          disabled={disabled || readonly}
          onChange={onChange}
          onDelete={handleDeleteItem}
          schema={
            isSingleFieldOf(schema.of)
              ? schema.of.type
              : Array.isArray(schema.of)
                ? schema.of[schemaIndex]
                : schema.of === 'object'
                  ? schema.fields
                  : schema.of
          }
          onClose={() => setSelected(undefined)}
        />
      )}
      {!value.length && (
        <SilkeBox>
          <SilkeText color="neutral-40">No data</SilkeText>
        </SilkeBox>
      )}
      {value.length > 0 && (
        <SilkeList
          items={listItems}
          size="s"
          sortable
          disabled={disabled || readonly}
          onSort={(sorted) => {
            const reordered = (sorted as SilkeListItemData[]).map((item) => {
              const idx = listItems.indexOf(item);
              return value[idx];
            });
            onChange(reordered);
          }}
        />
      )}
    </SilkeBox>
  );
};

export default ArrayField;
