import React, { useState, useEffect, useRef } from 'react';
import { buildJsonApiUrl } from './utils/urlBuilder';
import { useRelatedEntityFields } from './hooks/useRelatedEntityFields';
import { useEntityTypes } from './hooks/useEntityTypes';
import { useNotifications } from './hooks/useNotifications';
import { useApiRequest } from './hooks/useApiRequest';
import { useRequestHistory } from './hooks/useRequestHistory';
import { parseUrlToParameters } from './utils/urlParser';
import { copyToClipboard as copyToClipboardUtil } from './utils/clipboard';
import { storage, STORAGE_KEYS } from './utils/localStorage';

import TabNavigation from './components/TabNavigation.jsx';
import './resizable-panels.css';

function App({ baseUrl = '/jsonapi/', initialActiveTab = 0 }) {
  // Extract the site base URL without jsonapi endpoint for examples
  // Handle both default and custom JSON:API paths
  const jsonapiPathPattern = new RegExp(`${baseUrl.replace(/^\/|\/$/g, '')}/?$`);
  const siteBaseUrl = baseUrl.replace(jsonapiPathPattern, '');

  // Panel layout state
  const [activeTab, setActiveTab] = useState(initialActiveTab);
  const [expandedExample, setExpandedExample] = useState('filtering');
  const [builderPanelActiveTab, setBuilderPanelActiveTab] = useState('fields'); // Default to fields tab

  // Default panel sizes with stored values if available
  const savedPanelSizes = storage.get(STORAGE_KEYS.PANEL_LAYOUT, [20, 45, 35]);
  const [panelSizes, setPanelSizes] = useState(savedPanelSizes);

  // Load selections from localStorage with fallbacks to defaults
  const [selectedEntity, setSelectedEntity] = useState(() => storage.get(STORAGE_KEYS.SELECTED_ENTITY, ''));
  const [selectedBundle, setSelectedBundle] = useState(() => storage.get(STORAGE_KEYS.SELECTED_BUNDLE, ''));
  const [selectedFields, setSelectedFields] = useState(() => storage.get(STORAGE_KEYS.SELECTED_FIELDS, []));
  const [includes, setIncludes] = useState(() => storage.get(STORAGE_KEYS.INCLUDES, []));
  const [filters, setFilters] = useState(() => storage.get(STORAGE_KEYS.FILTERS, []));
  const [sorts, setSorts] = useState(() => storage.get(STORAGE_KEYS.SORTS, []));
  const [page, setPage] = useState(() => storage.get(STORAGE_KEYS.PAGE, 0));
  const [limit, setLimit] = useState(() => storage.get(STORAGE_KEYS.LIMIT, 10));

  const [requestUrl, setRequestUrl] = useState('');

  // Load initial auth token from localStorage
  const [authToken, setAuthToken] = useState(() => storage.get(STORAGE_KEYS.AUTH_TOKEN, ''));

  // Auto-execute preference (default to false)
  const [autoExecute, setAutoExecute] = useState(() => storage.get(STORAGE_KEYS.AUTO_EXECUTE, false));

  // Use custom hooks
  const { entities, loading: loadingEntities, error: entitiesError } = useEntityTypes(baseUrl);

  const { relatedEntityFields, loadingRelatedFields, setRelatedEntityFields } = useRelatedEntityFields(
      selectedEntity,
      selectedBundle,
      includes,
      baseUrl
  );

  const { notification, showNotification, handleCloseNotification, NotificationComponent } = useNotifications();

  // Set up state setter functions for history loading
  const setters = {
    setSelectedEntity,
    setSelectedBundle,
    setSelectedFields,
    setIncludes,
    setFilters,
    setSorts,
    setPage,
    setLimit
  };

  const {
    requestHistory,
    addToHistory,
    loadFromHistory
  } = useRequestHistory(setters);

  const {
    response,
    responseHeaders,
    responseSize,
    responseTime,
    loading: loadingRequest,
    error: requestError,
    executeRequest,
    setError
  } = useApiRequest(addToHistory);

  // Convenience wrapper for copyToClipboard
  const copyToClipboard = (text) => {
    copyToClipboardUtil(text, showNotification);
  };

  // Use a ref to track if includes have been updated by user action
  const userActionRef = useRef(false);
  // Use ref to track if this is the initial render/load from saved state
  const initialLoadRef = useRef(true);

  // Add a separate effect to handle changes to includes
  // to clean up fields when relationships are removed
  const prevIncludesRef = useRef([]);

  useEffect(() => {
    // If entity and bundle are selected and it's not the initial render
    if (selectedEntity && selectedBundle) {
      // Capture current and previous includes for comparison
      const currentIncludes = new Set(includes);
      const prevIncludes = new Set(prevIncludesRef.current);

      // Find relationships that were removed (in prev but not in current)
      const removedRelationships = prevIncludesRef.current.filter(
          rel => !currentIncludes.has(rel)
      );

      // Find relationships that were added (in current but not in prev)
      const addedRelationships = includes.filter(
          rel => !prevIncludes.has(rel)
      );

      // Store current includes for next comparison
      prevIncludesRef.current = [...includes];

      // Handle removed relationships - clean up related fields
      if (removedRelationships.length > 0) {
        console.log('Includes removed, cleaning up fields for:', removedRelationships);

        // Clean up selected fields
        setSelectedFields(prevFields =>
            prevFields.filter(field =>
                !removedRelationships.some(rel => field.startsWith(`${rel}.`))
            )
        );

        // Clean up filters
        setFilters(prevFilters =>
            prevFilters.filter(filter =>
                !removedRelationships.some(rel => filter.field?.startsWith(`${rel}.`))
            )
        );

        // Clean up sorts
        setSorts(prevSorts =>
            prevSorts.filter(sort =>
                !removedRelationships.some(rel => sort.field?.startsWith(`${rel}.`))
            )
        );

        // Remove includes from related entity fields
        console.log('Removed includes:', removedRelationships);
      }

      // For newly added includes, show notification
      if (addedRelationships.length > 0 && userActionRef.current) {
        console.log('New includes added by user action:', addedRelationships);

        // Format the notification message
        let message;
        if (addedRelationships.length === 1) {
          message = `Added "${addedRelationships[0]}" include. New fields, filters, and sort criteria are now available.`;
        } else if (addedRelationships.length === 2) {
          message = `Added "${addedRelationships[0]}" and "${addedRelationships[1]}" includes. New fields, filters, and sort criteria are now available.`;
        } else {
          message = `Added ${addedRelationships.length} includes. New fields, filters, and sort criteria are now available.`;
        }

        // Force a clear and reload of include data
        setRelatedEntityFields({}) // Clear current fields

        // Show the notification after a short delay
        setTimeout(() => {
          showNotification(message, 'status');
        }, 100);
      }

      // After the first update, mark initial load as complete
      if (initialLoadRef.current) {
        console.log('Initial load complete, future include changes will show notifications');
        initialLoadRef.current = false;
      }

      // Reset the user action flag
      userActionRef.current = false;
    }
  }, [includes, selectedEntity, selectedBundle]);

  // Use refs outside useEffect for tracking previous values
  const prevEntityRef = useRef(selectedEntity);
  const prevBundleRef = useRef(selectedBundle);

  // Reset filters, fields, etc. when entity type or bundle changes
  useEffect(() => {
    // Check if entity or bundle changed and we had previous values
    if ((prevEntityRef.current || prevBundleRef.current) && // Had previous values
        (prevEntityRef.current !== selectedEntity || prevBundleRef.current !== selectedBundle) && // Change detected
        (selectedEntity && selectedBundle)) { // Have valid new values

      console.log('Entity or bundle changed, resetting parameters');
      setSelectedFields([]);
      setIncludes([]);
      setFilters([]);
      setSorts([]);
      setPage(0);
      setLimit(10);
    }

    // Update refs for next check
    prevEntityRef.current = selectedEntity;
    prevBundleRef.current = selectedBundle;
  }, [selectedEntity, selectedBundle]);


  useEffect(() => {
    // Build the URL when any of the query parameters change
    buildUrl();
  }, [selectedEntity, selectedBundle, selectedFields, includes, filters, sorts, page, limit]);

  // Add auto-execute effect with debounce to prevent too many API calls
  useEffect(() => {
    if (autoExecute && requestUrl) {
      // Use timeout to debounce requests (wait 300ms after changes before executing)
      const timeout = setTimeout(() => {
        executeRequest(requestUrl);
      }, 300);

      // Clean up timeout on component unmount or before next run
      return () => clearTimeout(timeout);
    }
  }, [requestUrl, autoExecute]);

  // Persist auth token whenever it changes
  useEffect(() => {
    if (authToken !== undefined) {
      storage.set(STORAGE_KEYS.AUTH_TOKEN, authToken);
    }
  }, [authToken]);

  // Persist entity and bundle selections
  useEffect(() => {
    if (selectedEntity !== undefined) {
      storage.set(STORAGE_KEYS.SELECTED_ENTITY, selectedEntity);
    }
  }, [selectedEntity]);

  useEffect(() => {
    if (selectedBundle !== undefined) {
      storage.set(STORAGE_KEYS.SELECTED_BUNDLE, selectedBundle);
    }
  }, [selectedBundle]);

  // Ensure that when a field like uid.mail is selected, the uid relationship is also included
  const ensureRelationshipFieldsHaveIncludes = (fields, currentIncludes) => {
    const newIncludes = [...currentIncludes];
    let includesChanged = false;

    // Check each field for relationship.field pattern
    fields.forEach(field => {
      const parts = field.split('.');
      if (parts.length > 1) {
        const relationship = parts[0];
        // If this relationship is not already in includes, add it
        if (!newIncludes.includes(relationship)) {
          newIncludes.push(relationship);
          includesChanged = true;
          console.log(`Auto-including relationship "${relationship}" because field "${field}" is selected`);
        }
      }
    });

    return includesChanged ? newIncludes : currentIncludes;
  };

  // Persist fields, includes, filters and sorts
  useEffect(() => {
    // Check if we need to automatically include any relationships
    const newIncludes = ensureRelationshipFieldsHaveIncludes(selectedFields, includes);

    // Only update includes if they've changed (to avoid infinite loop)
    if (JSON.stringify(newIncludes) !== JSON.stringify(includes)) {
      // Update includes state
      setIncludes(newIncludes);
    }

    storage.set(STORAGE_KEYS.SELECTED_FIELDS, selectedFields);
  }, [selectedFields]);

  useEffect(() => {
    storage.set(STORAGE_KEYS.INCLUDES, includes);
  }, [includes]);

  useEffect(() => {
    // Check if any filters use relationship fields and ensure those relationships are included
    const filterFields = filters.map(filter => filter.field).filter(Boolean);
    const newIncludes = ensureRelationshipFieldsHaveIncludes(filterFields, includes);

    // Only update includes if they've changed
    if (JSON.stringify(newIncludes) !== JSON.stringify(includes)) {
      setIncludes(newIncludes);
    }

    storage.set(STORAGE_KEYS.FILTERS, filters);
  }, [filters]);

  useEffect(() => {
    // Check if any sorts use relationship fields and ensure those relationships are included
    const sortFields = sorts.map(sort => sort.field).filter(Boolean);
    const newIncludes = ensureRelationshipFieldsHaveIncludes(sortFields, includes);

    // Only update includes if they've changed
    if (JSON.stringify(newIncludes) !== JSON.stringify(includes)) {
      setIncludes(newIncludes);
    }

    storage.set(STORAGE_KEYS.SORTS, sorts);
  }, [sorts]);

  // Persist pagination options
  useEffect(() => {
    storage.set(STORAGE_KEYS.PAGE, page);
  }, [page]);

  useEffect(() => {
    storage.set(STORAGE_KEYS.LIMIT, limit);
  }, [limit]);

  // Persist autoExecute preference
  useEffect(() => {
    storage.set(STORAGE_KEYS.AUTO_EXECUTE, autoExecute);
  }, [autoExecute]);

  // Save panel sizes to localStorage when they change
  const handlePanelResize = (sizes) => {
    setPanelSizes(sizes);
    storage.set(STORAGE_KEYS.PANEL_LAYOUT, sizes);
  };

  const buildUrl = async () => {
    if (!selectedEntity || !selectedBundle) {
      setRequestUrl('');
      return;
    }

    // Use the extracted URL builder utility
    const url = await buildJsonApiUrl({
      selectedEntity,
      selectedBundle,
      selectedFields,
      includes,
      filters,
      sorts,
      page,
      limit,
      relatedEntityFields,
      baseUrl,
      showNotification
    });

    setRequestUrl(url);
  };

  const handleLoadFromHistory = (historyItem) => {
    const url = loadFromHistory(historyItem);
    setRequestUrl(url);
  };

  // Combine loading states
  const isLoading = loadingEntities || loadingRelatedFields || loadingRequest;

  // Combine error states
  const error = entitiesError || requestError;

  // Prepare props for ExplorerLayout
  const explorerLayoutProps = {
    // Panel layout
    panelSizes,
    handlePanelResize,

    // Entity and bundle selection
    entities,
    selectedEntity,
    selectedBundle,
    setSelectedEntity,
    setSelectedBundle,

    // Fields and relationships
    selectedFields,
    setSelectedFields,
    includes,
    setIncludes,
    relatedEntityFields,
    loadingRelatedFields,
    userActionRef,

    // Filters and sorts
    filters,
    setFilters,
    sorts,
    setSorts,

    // Pagination
    page,
    setPage,
    limit,
    setLimit,

    // URL and execution
    requestUrl,
    copyToClipboard,
    executeRequest: () => executeRequest(requestUrl),
    loading: isLoading,
    autoExecute,
    setAutoExecute,

    // Response
    error,
    response,
    responseHeaders,
    responseSize,
    responseTime,

    // Builder panel state
    builderPanelActiveTab,
    setBuilderPanelActiveTab,

    // History
    requestHistory,
    onLoadHistory: handleLoadFromHistory,

    // Notifications
    showNotification,

    // Base URL
    baseUrl
  };

  return (
      <div className="jsonapi-explorer-container">
        <div className="layout-container">
          <TabNavigation
              activeTab={activeTab}
              setActiveTab={setActiveTab}
              explorerLayoutProps={explorerLayoutProps}
              expandedExample={expandedExample}
              setExpandedExample={setExpandedExample}
              copyToClipboard={copyToClipboard}
              baseUrl={baseUrl}
          />
        </div>
        <NotificationComponent />
      </div>
  );
}

export default App;
