/**
 * RelationSelect widget component for foreign key relationships.
 *
 * ⚠️ AUTO-GENERATED BY PRISM - DO NOT EDIT
 */

import { useState, useEffect, useMemo, type MouseEvent } from 'react';
import { useQuery } from 'urql';
import type { WidgetBaseProps } from '../types';

interface RelationOption {
  value: string | number;
  label: string;
}

interface RelationSelectProps extends WidgetBaseProps<string | number | null> {
  /** Options for the select (can be static or loaded) */
  options?: RelationOption[];
  /** Async function to load options */
  loadOptions?: (search: string) => Promise<RelationOption[]>;
  /** Model name for automatic option loading (e.g., "Account", "Instrument") */
  references?: string;
  /** Field to display in the option label */
  labelField?: string;
  /** Allow clearing the selection */
  clearable?: boolean;
  /** Enable search/filter */
  searchable?: boolean;
}

/** Convert model name to GraphQL query name (e.g., "Account" -> "accounts") */
function toQueryName(modelName: string): string {
  // Simple pluralization: add 's' or 'es'
  const name = modelName.charAt(0).toLowerCase() + modelName.slice(1);
  if (name.endsWith('s') || name.endsWith('x') || name.endsWith('ch') || name.endsWith('sh')) {
    return name + 'es';
  }
  return name + 's';
}

/** Build a minimal GraphQL query for fetching relation options */
function buildOptionsQuery(modelName: string, labelField: string): string {
  const queryName = toQueryName(modelName);
  return `
    query ${modelName}Options {
      ${queryName}(pagination: { page: 1, pageSize: 100 }) {
        edges {
          node {
            id
            ${labelField}
          }
        }
      }
    }
  `;
}

export function RelationSelect({
  name,
  value,
  onChange,
  label,
  description,
  placeholder = 'Select...',
  required,
  disabled,
  error,
  className,
  options: staticOptions = [],
  loadOptions,
  references,
  labelField = 'name',
  clearable = true,
  searchable = true,
}: RelationSelectProps): JSX.Element {
  // Memoize staticOptions to avoid infinite re-renders from array reference changes
  const stableStaticOptions = useMemo(
    () => staticOptions,
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [JSON.stringify(staticOptions)]
  );

  // Build query for referenced model if references is provided
  const query = useMemo(
    () => (references ? buildOptionsQuery(references, labelField) : null),
    [references, labelField]
  );

  // Query referenced model for options
  const [queryResult] = useQuery({
    query: query || '',
    pause: !query, // Don't run if no references
  });

  // Extract options from query result
  const referencedOptions = useMemo<RelationOption[]>(() => {
    if (!references || !queryResult.data) return [];
    const queryName = toQueryName(references);
    const connection = queryResult.data[queryName];
    if (!connection?.edges) return [];
    return connection.edges.map((edge: { node: Record<string, unknown> }) => ({
      value: edge.node.id as number,
      label: String(edge.node[labelField] || edge.node.id),
    }));
  }, [references, queryResult.data, labelField]);

  const [options, setOptions] = useState<RelationOption[]>(stableStaticOptions);
  const [searchTerm, setSearchTerm] = useState('');
  const [isOpen, setIsOpen] = useState(false);
  const [isLoading, setIsLoading] = useState(false);

  // Update options when referenced options change
  useEffect(() => {
    if (references && referencedOptions.length > 0) {
      setOptions(referencedOptions);
    }
  }, [references, referencedOptions]);

  // Load options when search term changes (for async loading)
  useEffect(() => {
    if (references) {
      // Options are loaded via GraphQL query, no need to use loadOptions
      return;
    }

    if (!loadOptions) {
      setOptions(stableStaticOptions);
      return;
    }

    const loadData = async () => {
      setIsLoading(true);
      try {
        const result = await loadOptions(searchTerm);
        setOptions(result);
      } catch {
        setOptions([]);
      } finally {
        setIsLoading(false);
      }
    };

    const debounce = setTimeout(loadData, 300);
    return () => clearTimeout(debounce);
  }, [searchTerm, loadOptions, stableStaticOptions, references]);

  // Filter options based on search (for static options or referenced options)
  const filteredOptions = (!loadOptions || references) && searchable
    ? options.filter((opt) =>
        opt.label.toLowerCase().includes(searchTerm.toLowerCase())
      )
    : options;

  // Consider query loading state
  const effectiveLoading = isLoading || (references && queryResult.fetching);

  const selectedOption = options.find((opt) => opt.value === value);

  const handleSelect = (option: RelationOption) => {
    onChange(option.value);
    setIsOpen(false);
    setSearchTerm('');
  };

  const handleClear = (e: MouseEvent) => {
    e.stopPropagation();
    onChange(null);
    setSearchTerm('');
  };

  return (
    <div className={`${className} relative`}>
      {label && (
        <label htmlFor={name} className="label">
          {label}
          {required && <span className="text-red-500 ml-1">*</span>}
        </label>
      )}
      <div
        className={`relative ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
        onClick={() => !disabled && setIsOpen(!isOpen)}
      >
        <div
          className={`flex items-center justify-between px-3 py-2 rounded-lg border ${
            error
              ? 'border-red-500'
              : isOpen
              ? 'border-nordic-500 ring-2 ring-nordic-500'
              : 'border-nordic-300'
          } bg-white`}
        >
          {isOpen && searchable ? (
            <input
              type="text"
              value={searchTerm}
              onChange={(e) => setSearchTerm(e.target.value)}
              placeholder={placeholder}
              className="flex-1 border-0 p-0 focus:ring-0 focus:outline-none text-sm"
              onClick={(e) => e.stopPropagation()}
              autoFocus
            />
          ) : (
            <span className={selectedOption ? 'text-nordic-900' : 'text-nordic-400'}>
              {selectedOption?.label ?? placeholder}
            </span>
          )}
          <div className="flex items-center gap-1">
            {clearable && value !== null && value !== undefined && !disabled && (
              <button
                type="button"
                onClick={handleClear}
                className="text-nordic-400 hover:text-nordic-600"
              >
                <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
                </svg>
              </button>
            )}
            <svg
              className={`w-5 h-5 text-nordic-400 transition-transform ${isOpen ? 'rotate-180' : ''}`}
              fill="none"
              viewBox="0 0 24 24"
              stroke="currentColor"
            >
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
            </svg>
          </div>
        </div>

        {isOpen && (
          <div className="absolute z-10 w-full mt-1 bg-white border border-nordic-300 rounded-lg shadow-lg max-h-60 overflow-auto">
            {effectiveLoading ? (
              <div className="px-3 py-2 text-sm text-nordic-500">Loading...</div>
            ) : filteredOptions.length === 0 ? (
              <div className="px-3 py-2 text-sm text-nordic-500">No options found</div>
            ) : (
              filteredOptions.map((option) => (
                <div
                  key={option.value}
                  onClick={(e) => {
                    e.stopPropagation();
                    handleSelect(option);
                  }}
                  className={`px-3 py-2 text-sm cursor-pointer hover:bg-nordic-50 ${
                    option.value === value ? 'bg-nordic-100 text-nordic-900' : 'text-nordic-700'
                  }`}
                >
                  {option.label}
                </div>
              ))
            )}
          </div>
        )}
      </div>
      {description && !error && (
        <p id={`${name}-desc`} className="mt-1.5 text-sm text-nordic-500">{description}</p>
      )}
      {error && (
        <p id={`${name}-error`} className="mt-1.5 text-sm text-red-600">
          {error}
        </p>
      )}
    </div>
  );
}
