import { 
  useEffect, 
  useRef, 
  useState 
} from 'react';
import { useOutletContext } from 'react-router-dom';
import { 
  Controller, 
  useForm 
} from 'react-hook-form';
import { Col } from 'react-bootstrap';

// components

import CheckboxInput from "../formElements/CheckBoxInput.Controlled";
import Reset from "./Reset";
import Selector from "../formElements/Selector.Controlled";
import TextInput from "../formElements/TextInput";
import TextArea from "../formElements/TextArea";

// hooks

import useDebounceLogic from '../../hooks/utilities/useDebounceLogic';

// functions

import apiLoader from '../../../services/utilities/apiLoader';

// constants

const errorNumber = 633;
const nowRunning = 'common/display/CustomFields';

function CustomFields({ 
  category,
  criteria,
  globalPrompt='make this change globally',
  setCustomFields, 
  targetItem,
}) { 

  // Use a separate form instance for custom fields. This allows all of the React Hook form magic.

  const { 
    control, 
    getValues, 
    setValue 
  } = useForm();

  // Local state for field definitions and loaded flag.

  const [debouncedCriteria, setDebouncedCriteria] = useState(criteria);
  const [fieldDefs, setFieldDefs] = useState({});
  const [loaded, setLoaded] = useState(false);

  // Use a ref to store previous lifted values and avoid unnecessary updates.

  const prevValuesRef = useRef({});

  const {
    appReady,
    customFieldsData,
    handleError,
    setCustomFieldsLoaded
  } = useOutletContext();

  const debouncedSetter = useDebounceLogic(setDebouncedCriteria, 1000);

  useEffect(
    
    () => {
      debouncedSetter(criteria);
    }, 
    [
      criteria, 
      debouncedSetter
    ]  
  
  );

  useEffect(

    () => {

      if (!appReady) return; // New items do not need to run this effect.
      
      const context = `${nowRunning}.useEffect`;

      const runThis = async () => { 

        let currentCustomValues = {};
      
        if (targetItem) { // Load the custom fields data for the target item. This is for existing items.
      
          const result = await apiLoader({
            api: 'utilities/fields/data/get-item-data',
            handleError,
            payload: { targetId: targetItem }
          });
          const {
            failure = 'no failure message was returned',
            success
          } = result?.data;
          currentCustomValues = result?.data?.currentCustomValues;
      
          if (!success) {
            
            handleError(failure, context, errorNumber, true);
            return;
      
          }
      
        } else if (typeof criteria === 'string' && criteria.trim().length >= 3 ) { // Load the most recent recently updated item with the given criteria.

          let lastItemId = '';
      
          switch(category) {

            case 2: { // inventory items

              const result = await apiLoader({
                api: 'inventory/items/lookup-last-item',
                handleError,
                payload: { sku: criteria }
              });
              const {
                failure = 'no failure message was returned',
                success
              } = result?.data;
              lastItemId = result?.data?.lastItemId;

              if (!success) {

                handleError(failure, context, errorNumber, true);
                return;

              }

              break;
            
            }
      
            default: {
        
              break;
        
            }

          }

          if (lastItemId) {

            const result = await apiLoader({
              api: 'utilities/fields/data/get-item-data',
              handleError,
              payload: { targetId: lastItemId }
            }); 
            const {
              failure = 'no failure message was returned',
              success
            } = result?.data;
            currentCustomValues = result?.data?.currentCustomValues;

            if (!success) {

              handleError(failure, context, errorNumber, true);
              return;

            }

          }

        }
      
        let updatedFields = {}; // Make sure this is always an object (even if empty) at the end of the switch cases!!
      
        switch(category) {
      
          case 2: { // inventory items
      
            updatedFields = customFieldsData.inventoryCustomFields || {};
            break;
      
          }
      
          default: {
      
            break;
      
          }
      
        }
      
        // Filter out inactive fields.
      
        updatedFields = Object.fromEntries(
          Object.entries(updatedFields).filter(([_, field]) => field.active)
        );
      
        if (Object.keys(updatedFields).length < 1) return; // This category has no active custom fields, so there's nothing to display.
        
        // Build initialValues using the API data and the field definitions. NOTE: data is undefined if there is no targetItem (new items).
        // For each field in updatedFields, if there is an API value, then:
        // - For guiType 1, 3, or 4 (Selector, TextInput, TextArea): take the first element’s uuidValue (or textValue, as appropriate)
        // - For guiType 2 (checkbox group): map the array to an array of uuidValues.
      
        const initialValues = {};
      
        for (const [fieldId, field] of Object.entries(currentCustomValues)) {
      
          const apiValues = currentCustomValues?.[fieldId];
      
          if (apiValues && Array.isArray(apiValues) && apiValues.length > 0) {

            const guiType = updatedFields[fieldId].guiType; // What we did here was go back to the field metadata to understand the type of field.
      
            if (guiType === 1 || guiType === 3 || guiType === 4) {
      
              // For single-value fields, use the first value’s uuidValue.
      
              initialValues[fieldId] = apiValues[0].uuidValue || "";
      
            } else if (guiType === 2) {  // For checkbox groups, use an array of uuidValues.
      
              initialValues[fieldId] = apiValues.map(item => item.uuidValue);
      
            }
      
          } else { // No API value: use default from field definition or a fallback.
      
            initialValues[fieldId] = field.defaultValue 
              ? field.defaultValue 
              : (field.guiType === 2 ? [] : "");
      
          }
      
        }
                
        setCustomFields(initialValues); // Lift initial custom field values up to the parent.
      
        // Update local form state with these defaults.
      
        if (Object.keys(initialValues).length > 0) {
          Object.entries(initialValues).forEach(([key, val]) => {
            setValue(key, val);
          });
        } else {

          Object.keys(updatedFields).forEach((fieldId) => {
            const guiType = updatedFields[fieldId]?.guiType;
          
            const defaultVal =
              guiType === 2 ? [] : ""; // checkbox group = [], others = ""
          
            setValue(fieldId, defaultVal);
          });
          setValue("global", false); // reset global manually too

        }
      
        // Store the initial lifted values.
      
        prevValuesRef.current = initialValues;
        setFieldDefs(updatedFields);
        setLoaded(true);
      
      };

      runThis().catch(e => handleError(e, context, errorNumber));

    }, 
    [
      appReady,
      category,
      criteria,
      customFieldsData,
      debouncedCriteria,
      handleError,
      setCustomFields,
      setValue,
      targetItem
    ] 

  );

  // When the target item changes, reset the global field to false.

  useEffect(
    
    () => {

      if (targetItem) {

        setValue("global", false);
        setCustomFields(prev => ({
          ...prev,
          global: false
        }));
      }
    }, 
    [
      setCustomFields, 
      setValue,      
      targetItem
    ]

  );

  // Helper function to lift values on change if they've actually changed.

  const liftValues = () => {

    const newValues = getValues();

    if (JSON.stringify(newValues) !== JSON.stringify(prevValuesRef.current)) {

      setCustomFields(newValues);
      prevValuesRef.current = newValues;

    }

  };

  if (!loaded || !appReady) return null;

  if (Object.keys(fieldDefs).length < 1) return null;

  return (

    <>

      <div className="size-65 mb-3"><b>custom fields</b></div>

      {Object.entries(fieldDefs).map(([fieldId, field]) => {

        const { 
          fieldName, 
          guiType, 
          placeholder, 
          presets 
        } = field;

        // Filter active presets if available.

        const activePresets = presets 
          ? Object.entries(presets).filter(([_, preset]) => preset.presetActive)
          : [];

        // If there are no active presets, skip this field.

        if (activePresets.length < 1) return null;

        switch (guiType) {

          case 1: { // Selector field (expects a scalar value).

            const sortedPresets = activePresets
              .sort(([, a], [, b]) => a.position - b.position)
              .reduce((acc, [presetKey, preset]) => {

                acc[presetKey] = preset.fieldValue;
                return acc;

              }, {});

            return (

              <Col 
                xs={12} sm={6} 
                key={fieldId}
              >
                <Controller
                  name={fieldId}
                  control={control}
                  defaultValue=""
                  render={({ field: { onChange, value } }) => (
                    <Selector
                      inputName={fieldId}
                      label={fieldName}
                      placeholder={placeholder}
                      values={sortedPresets}
                      value={value}
                      onChange={async e => {

                        onChange(e.target.value);
                        liftValues();

                      }}
                    />
                  )}
                />
              </Col>

            );
          }
          case 2: { // Checkbox group (expects an array).

            return (
              <Col 
                xs={12} sm={activePresets.length > 10 ? 12 : 6} 
                key={fieldId}
              >

                <div className="size-65 mb-3">{fieldName}</div>

                <div className="floats">
                  <Controller
                    name={fieldId}
                    control={control}
                    defaultValue={[]}
                    render={({ field: { onChange, value } }) => {
                      const handleCheckboxChange = (presetId, checked) => {

                        let newValue = Array.isArray(value) ? [...value] : [];

                        if (checked) {

                          if (!newValue.includes(presetId)) newValue.push(presetId);

                        } else {

                          newValue = newValue.filter(id => id !== presetId);

                        }

                        onChange(newValue);
                        liftValues();

                      };
                      return activePresets.map(([presetId, preset]) => (
                        <div className="float-left mr-1" key={presetId}>
                          <CheckboxInput
                            inputName={`${fieldId}_${presetId}`}
                            label={preset.fieldValue}
                            checked={Array.isArray(value) && value.includes(presetId)}
                            onChange={e => handleCheckboxChange(presetId, e.target.checked)}
                          />
                        </div>
                      ));
                    }}
                  />
                </div>

              </Col>
            );
          }
          case 3: { // Text input.

            return (
              <Col xs={12} sm={6} key={fieldId}>
                <Controller
                  name={fieldId}
                  control={control}
                  defaultValue=""
                  render={({ field: { onChange, value } }) => (
                    <TextInput
                      inputName={fieldId}
                      label={fieldName}
                      placeholder={placeholder}
                      value={value}
                      onChange={e => {

                        onChange(e.target.value);
                        liftValues();

                      }}
                    />
                  )}
                />
              </Col>
            );
          }
          case 4: { // Text area.

            return (
              <Col xs={12} key={fieldId}>
                <Controller
                  name={fieldId}
                  control={control}
                  defaultValue=""
                  render={({ field: { onChange, value } }) => (
                    <TextArea
                      inputName={fieldId}
                      label={fieldName}
                      placeholder={placeholder}
                      value={value}
                      onChange={e => {

                        onChange(e.target.value);
                        liftValues();

                      }}
                    />
                  )}
                />
              </Col>
            );
          }

          default:

            return null;

        }

      })}

      <Col xs={12} sm={6}>

        <div className="size-65 mb-3">options</div>

        <div className="floats">

          <div className="float-left mr-1">

            <Controller
              name="global"
              control={control}
              defaultValue={false}
              render={({ field: { onChange, value } }) => (
                <CheckboxInput
                  inputName="global"
                  label={globalPrompt}
                  checked={value}
                  onChange={e => {
                    const newVal = e.target.checked;
                    onChange(newVal);
                    liftValues();
                  }}
                />
              )}
            />

          </div>

          <div className="float-left">

            <Reset 
              onReset={() => setCustomFieldsLoaded(false)} 
              prompt="refresh custom fields"
            />

          </div>

        </div>

      </Col>

    </>

  );

}

export default CustomFields;