import { useState, useEffect, useCallback } from 'react';
import { apiRequest } from '../services/api';
import { SelectOption } from '../types/schema';

/**
 * Pending fetch request item
 */
interface PendingFetch {
  fieldName: string;
  endpoint: string;
  dependsOnValue?: string;
  config?: Record<string, unknown>;
}

/**
 * Return type for useDynamicOptions hook
 */
export interface UseDynamicOptionsReturn {
  /** Cached options keyed by field name or cache key */
  dynamicOptions: Record<string, SelectOption[]>;
  /** Loading state keyed by field name or cache key */
  loadingFields: Record<string, boolean>;
  /** Error messages keyed by field name or cache key */
  fieldErrors: Record<string, string>;
  /** Queue a fetch request (executed in useEffect to avoid render side-effects) */
  queueFetch: (
    fieldName: string,
    endpoint: string,
    dependsOnValue?: string,
    config?: Record<string, unknown>,
  ) => void;
  /** Get options with dependency-aware cache key */
  getOptions: (fieldName: string, dependsOnValue?: string) => SelectOption[] | undefined;
  /** Check if options are loading with dependency-aware cache key */
  isLoading: (fieldName: string, dependsOnValue?: string) => boolean;
  /** Get error for field with dependency-aware cache key */
  getError: (fieldName: string, dependsOnValue?: string) => string | undefined;
}

/**
 * Custom hook for managing dynamic field options
 *
 * Handles fetching and caching of options for dynamic select fields
 * in the ConfigPanel. Supports both simple endpoints and dependent
 * endpoints (where the URL includes placeholders for other field values).
 *
 * @example
 * ```tsx
 * const { dynamicOptions, loadingFields, queueFetch, getOptions, isLoading } = useDynamicOptions();
 *
 * // Queue a simple fetch
 * queueFetch('list_id', '/integrations/mailchimp/lists');
 *
 * // Queue a dependent fetch (e.g., custom fields based on list_id)
 * queueFetch('custom_fields', '/integrations/mailchimp/lists/{list_id}/merge-fields', listId, { list_id: listId });
 *
 * // Get options
 * const lists = getOptions('list_id');
 * const customFields = getOptions('custom_fields', listId);
 * ```
 */
export function useDynamicOptions(): UseDynamicOptionsReturn {
  const [dynamicOptions, setDynamicOptions] = useState<Record<string, SelectOption[]>>({});
  const [loadingFields, setLoadingFields] = useState<Record<string, boolean>>({});
  const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
  const [pendingFetches, setPendingFetches] = useState<PendingFetch[]>([]);

  /**
   * Generate cache key for field (includes dependency value if present)
   */
  const getCacheKey = useCallback((fieldName: string, dependsOnValue?: string) => {
    return dependsOnValue ? `${fieldName}_${dependsOnValue}` : fieldName;
  }, []);

  /**
   * Fetch simple options (no dependencies)
   */
  const fetchOptions = useCallback(
    async (fieldName: string, endpoint: string, config?: Record<string, unknown>) => {
      if (loadingFields[fieldName] || dynamicOptions[fieldName]) return;

      setLoadingFields((prev) => ({ ...prev, [fieldName]: true }));
      setFieldErrors((prev) => {
        const newErrors = { ...prev };
        delete newErrors[fieldName];
        return newErrors;
      });

      try {
        // Remove leading /sequensy/v1 if present
        let cleanEndpoint = endpoint.startsWith('/sequensy/v1')
          ? endpoint.replace('/sequensy/v1', '')
          : endpoint;

        // Replace placeholders with config values (e.g., {connection_id})
        if (config) {
          cleanEndpoint = cleanEndpoint.replace(/\{(\w+)\}/g, (match, key) => {
            const value = config[key];
            return value !== undefined && value !== null ? String(value) : match;
          });
        }

        const data = await apiRequest<SelectOption[]>(cleanEndpoint);
        setDynamicOptions((prev) => ({ ...prev, [fieldName]: data }));
      } catch (error) {
        console.error(`Error fetching options for ${fieldName}:`, error);
        setFieldErrors((prev) => ({
          ...prev,
          [fieldName]: error instanceof Error ? error.message : 'Failed to load options',
        }));
      } finally {
        setLoadingFields((prev) => ({ ...prev, [fieldName]: false }));
      }
    },
    [loadingFields, dynamicOptions],
  );

  /**
   * Fetch dependent options (URL has placeholders for config values)
   */
  const fetchDependentOptions = useCallback(
    async (
      fieldName: string,
      endpoint: string,
      dependsOnValue: string,
      config?: Record<string, unknown>,
    ) => {
      const cacheKey = getCacheKey(fieldName, dependsOnValue);

      if (loadingFields[cacheKey] || dynamicOptions[cacheKey]) return;

      setLoadingFields((prev) => ({ ...prev, [cacheKey]: true }));
      setFieldErrors((prev) => {
        const newErrors = { ...prev };
        delete newErrors[cacheKey];
        return newErrors;
      });

      try {
        // Replace placeholders with config values
        let resolvedEndpoint = endpoint;
        if (config) {
          resolvedEndpoint = endpoint.replace(/\{(\w+)\}/g, (match, key) => {
            const value = config[key];
            return value !== undefined && value !== null ? String(value) : match;
          });
        } else {
          // Fallback: replace all placeholders with dependsOnValue
          resolvedEndpoint = endpoint.replace(/\{[^}]+\}/g, dependsOnValue);
        }
        resolvedEndpoint = resolvedEndpoint.replace('/sequensy/v1', '');

        const data = await apiRequest<SelectOption[]>(resolvedEndpoint);
        setDynamicOptions((prev) => ({ ...prev, [cacheKey]: data }));
      } catch (error) {
        console.error(`Error fetching dependent options for ${fieldName}:`, error);
        setFieldErrors((prev) => ({
          ...prev,
          [cacheKey]: error instanceof Error ? error.message : 'Failed to load options',
        }));
      } finally {
        setLoadingFields((prev) => ({ ...prev, [cacheKey]: false }));
      }
    },
    [loadingFields, dynamicOptions, getCacheKey],
  );

  /**
   * Process pending fetches
   */
  useEffect(() => {
    if (pendingFetches.length === 0) return;

    pendingFetches.forEach(({ fieldName, endpoint, dependsOnValue, config }) => {
      if (dependsOnValue) {
        fetchDependentOptions(fieldName, endpoint, dependsOnValue, config);
      } else {
        fetchOptions(fieldName, endpoint, config);
      }
    });

    setPendingFetches([]);
  }, [pendingFetches, fetchDependentOptions, fetchOptions]);

  /**
   * Queue a fetch request (safe to call during render)
   */
  const queueFetch = useCallback(
    (
      fieldName: string,
      endpoint: string,
      dependsOnValue?: string,
      config?: Record<string, unknown>,
    ) => {
      setPendingFetches((prev) => {
        // Avoid duplicate entries
        const exists = prev.some(
          (p) =>
            p.fieldName === fieldName &&
            p.endpoint === endpoint &&
            p.dependsOnValue === dependsOnValue,
        );
        if (exists) return prev;
        return [...prev, { fieldName, endpoint, dependsOnValue, config }];
      });
    },
    [],
  );

  /**
   * Get options for a field
   */
  const getOptions = useCallback(
    (fieldName: string, dependsOnValue?: string) => {
      const cacheKey = getCacheKey(fieldName, dependsOnValue);
      return dynamicOptions[cacheKey];
    },
    [dynamicOptions, getCacheKey],
  );

  /**
   * Check if options are loading
   */
  const isLoading = useCallback(
    (fieldName: string, dependsOnValue?: string) => {
      const cacheKey = getCacheKey(fieldName, dependsOnValue);
      return loadingFields[cacheKey] || false;
    },
    [loadingFields, getCacheKey],
  );

  /**
   * Get error for a field
   */
  const getError = useCallback(
    (fieldName: string, dependsOnValue?: string) => {
      const cacheKey = getCacheKey(fieldName, dependsOnValue);
      return fieldErrors[cacheKey];
    },
    [fieldErrors, getCacheKey],
  );

  return {
    dynamicOptions,
    loadingFields,
    fieldErrors,
    queueFetch,
    getOptions,
    isLoading,
    getError,
  };
}
