import React, { ReactElement, useEffect, useMemo, useState } from 'react';

import { requestJson } from '../../utils';

type CategoryItem = {
  term_id: string | number;
  parent?: string | number;
  name?: string;
  path?: string;
};

type CategoryNode = {
  item: CategoryItem;
  children: CategoryNode[];
};

const FolderIcon = ({ color = '#afb8d4' }: { color?: string }): ReactElement => (
  <svg
    aria-hidden="true"
    width="18"
    height="18"
    viewBox="0 0 18 18"
    style={{ display: 'block' }}
  >
    <path
      d="M2 4.75h4.1l1.15 1.35H16c.55 0 1 .45 1 1v6.9c0 .55-.45 1-1 1H2c-.55 0-1-.45-1-1v-8.25c0-.55.45-1 1-1Z"
      fill={color}
    />
    <path
      d="M1.9 6.15h14.2c.5 0 .9.4.9.9v.25H1v-.25c0-.5.4-.9.9-.9Z"
      fill="rgba(255,255,255,0.24)"
    />
  </svg>
);

const normalizeId = (value: string | number | undefined): string => String(value ?? '');
const normalizeParentId = (value: string | number | undefined): string => {
  const parent = String(value ?? '0');
  return parent === '' ? '0' : parent;
};

const buildTree = (categories: CategoryItem[]): CategoryNode[] => {
  const nodeMap = new Map<string, CategoryNode>();
  const roots: CategoryNode[] = [];

  categories.forEach((item) => {
    nodeMap.set(normalizeId(item.term_id), {
      item,
      children: [],
    });
  });

  categories.forEach((item) => {
    const node = nodeMap.get(normalizeId(item.term_id));
    const parentId = normalizeParentId(item.parent);

    if (!node) {
      return;
    }

    if (parentId !== '0' && nodeMap.has(parentId)) {
      nodeMap.get(parentId)?.children.push(node);
      return;
    }

    roots.push(node);
  });

  return roots;
};

const collectExpandedIds = (nodes: CategoryNode[], expanded: Set<string>) => {
  nodes.forEach((node) => {
    if (node.children.length > 0) {
      expanded.add(normalizeId(node.item.term_id));
      collectExpandedIds(node.children, expanded);
    }
  });
};

const filterTree = (nodes: CategoryNode[], query: string): CategoryNode[] => {
  const normalizedQuery = query.trim().toLowerCase();

  if (!normalizedQuery) {
    return nodes;
  }

  return nodes.reduce<CategoryNode[]>((acc, node) => {
    const name = String(node.item.name ?? '').toLowerCase();
    const path = String(node.item.path ?? '').toLowerCase();
    const filteredChildren = filterTree(node.children, normalizedQuery);

    if (name.includes(normalizedQuery) || path.includes(normalizedQuery) || filteredChildren.length > 0) {
      acc.push({
        item: node.item,
        children: filteredChildren,
      });
    }

    return acc;
  }, []);
};

type TreeNodeProps = {
  currentCategoryId: string;
  depth?: number;
  expandedIds: Set<string>;
  readonly?: boolean;
  onSelect: (item: CategoryItem) => void;
  onToggle: (id: string) => void;
  node: CategoryNode;
};

const TreeNode = ({
  currentCategoryId,
  depth = 0,
  expandedIds,
  readonly,
  onSelect,
  onToggle,
  node,
}: TreeNodeProps): ReactElement => {
  const nodeId = normalizeId(node.item.term_id);
  const hasChildren = node.children.length > 0;
  const isExpanded = expandedIds.has(nodeId);
  const isActive = currentCategoryId === nodeId;

  return (
    <div>
      <button
        type="button"
        disabled={readonly}
        onClick={() => onSelect(node.item)}
        style={{
          display: 'flex',
          alignItems: 'center',
          gap: 8,
          width: '100%',
          textAlign: 'left',
          padding: '6px 10px',
          paddingLeft: `${10 + (depth * 18)}px`,
          border: 'none',
          background: isActive ? '#eef4ff' : 'transparent',
          borderRadius: 4,
          cursor: readonly ? 'default' : 'pointer',
          color: '#1d2327',
        }}
      >
        <span
          onClick={(event) => {
            if (!hasChildren) {
              return;
            }

            event.preventDefault();
            event.stopPropagation();
            onToggle(nodeId);
          }}
          style={{
            width: 12,
            color: hasChildren ? '#50575e' : 'transparent',
            cursor: hasChildren && !readonly ? 'pointer' : 'default',
            userSelect: 'none',
            flexShrink: 0,
          }}
        >
          {hasChildren ? (isExpanded ? '▾' : '▸') : '•'}
        </span>
        <span style={{ flexShrink: 0, display: 'inline-flex', alignItems: 'center' }}>
          <FolderIcon color={isActive ? '#8aa9e6' : '#afb8d4'} />
        </span>
        <span style={{ fontWeight: isActive ? 600 : 400 }}>{node.item.name || node.item.term_id}</span>
      </button>

      {hasChildren && isExpanded && (
        <div>
          {node.children.map((child) => (
            <TreeNode
              key={normalizeId(child.item.term_id)}
              currentCategoryId={currentCategoryId}
              depth={depth + 1}
              expandedIds={expandedIds}
              readonly={readonly}
              onSelect={onSelect}
              onToggle={onToggle}
              node={child}
            />
          ))}
        </div>
      )}
    </div>
  );
};

export const WpfdCategoryPickerField = (props: any): ReactElement => {
  const { value, onChange, readonly } = props;
  const [categories, setCategories] = useState<CategoryItem[]>([]);
  const [query, setQuery] = useState('');
  const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
  const [managerLink, setManagerLink] = useState('');

  useEffect(() => {
    requestJson({
      action: 'wpfd',
      task: 'categories.listCats',
    }).then((response) => {
      const nextCategories = Array.isArray(response?.data) ? response.data : [];
      setCategories(nextCategories);

      const initialExpanded = new Set<string>();
      collectExpandedIds(buildTree(nextCategories), initialExpanded);
      setExpandedIds(initialExpanded);
    }).catch(() => {
      setCategories([]);
    });
  }, []);

  useEffect(() => {
    requestJson({
      action: 'wpfd',
      task: 'category.editCategoryLink',
    }).then((response) => {
      setManagerLink(String(response?.data ?? ''));
    }).catch(() => {
      setManagerLink('');
    });
  }, []);

  const currentValue = useMemo(() => {
    if (value && typeof value === 'object') {
      return value;
    }

    return {
      categoryId: '',
      categoryPath: '',
    };
  }, [value]);

  const tree = useMemo(() => buildTree(categories), [categories]);
  const visibleTree = useMemo(() => filterTree(tree, query), [tree, query]);

  const toggleNode = (id: string) => {
    if (readonly) {
      return;
    }

    setExpandedIds((current) => {
      const next = new Set(current);

      if (next.has(id)) {
        next.delete(id);
      } else {
        next.add(id);
      }

      return next;
    });
  };

  const selectCategory = (item: CategoryItem) => {
    if (readonly) {
      return;
    }

    onChange({
      inputValue: {
        categoryId: normalizeId(item.term_id),
        categoryPath: String(item.path ?? ''),
      },
    });
  };

  return (
    <div style={{ border: '1px solid #dcdcde', borderRadius: 4, padding: 12, background: '#fff' }}>
      <input
        type="text"
        value={query}
        onChange={(event) => setQuery(event.target.value)}
        placeholder="Search for file category..."
        disabled={readonly}
        style={{
          width: '100%',
          marginBottom: 10,
          padding: '8px 10px',
          border: '1px solid #dcdcde',
          borderRadius: 4,
        }}
      />
      <div style={{ fontSize: 12, color: '#50575e', marginBottom: 10 }}>
        {currentValue.categoryId ? `Selected: ${currentValue.categoryPath || currentValue.categoryId}` : 'No category selected'}
      </div>
      <div
        style={{
          maxHeight: 260,
          overflowY: 'auto',
          border: '1px solid #dcdcde',
          borderRadius: 4,
          padding: 8,
          background: '#fff',
        }}
      >
        {visibleTree.length === 0 ? (
          <div style={{ padding: '8px 10px', color: '#646970', fontSize: 13 }}>No categories found.</div>
        ) : (
          visibleTree.map((node) => (
            <TreeNode
              key={normalizeId(node.item.term_id)}
              currentCategoryId={normalizeId(currentValue.categoryId)}
              expandedIds={expandedIds}
              readonly={readonly}
              onSelect={selectCategory}
              onToggle={toggleNode}
              node={node}
            />
          ))
        )}
      </div>
      {managerLink && (
        <div style={{ marginTop: 10, display: 'flex', justifyContent: 'flex-start' }}>
          <a
            href={managerLink}
            target="_blank"
            rel="noreferrer"
            style={{
              color: '#2271b1',
              fontSize: 12,
              fontWeight: 600,
              textDecoration: 'none',
            }}
          >
            Manage Files
          </a>
        </div>
      )}
    </div>
  );
};
