/* eslint-disable @typescript-eslint/no-explicit-any */
import get from 'lodash-es/get';
import { FieldString } from '@/TailwindPlay/Fields/String.js';
import { FieldS3Image } from '@/TailwindPlay/Fields/S3Image.js';
import { FieldContent } from '@/TailwindPlay/Fields/Content.js';
import { FieldLocalImage } from '@/TailwindPlay/Fields/LocalImage.js';
import { FieldTitle } from '@/TailwindPlay/Fields/Title.js';
import { FieldDate } from '@/TailwindPlay/Fields/Date.js';
import { FieldBoolean } from '@/TailwindPlay/Fields/Boolean.js';
import { TailwindPlayCommonParams } from '@/TailwindPlay/Fields/types.js';
import { FieldSelect } from '@/TailwindPlay/Fields/Select.js';
import { FieldRelation } from '@/TailwindPlay/Fields/Relation.js';
import { FieldNumber } from '@/TailwindPlay/Fields/Number.js';
import { FieldFile } from '@/TailwindPlay/Fields/File.js';
import { FieldS3Video } from '@/TailwindPlay/Fields/S3Video.js';
import { FieldInput } from '@/TailwindPlay/Fields/Input.js';
import { FieldButton } from '@/TailwindPlay/Fields//Button.js';
import { FieldCheckbox } from '@/TailwindPlay/Fields/Checkbox.js';
import { FieldVariable } from '@/TailwindPlay/Fields/Variable.js';
import { FieldLink } from '@/TailwindPlay/Fields/Link.js';
import { FieldScript } from '@/TailwindPlay/Fields/Script.js';
import type { FieldFrontType } from '@/graphql/core/selectors.js';
import React, { createElement } from 'react';
import { ActionType, CMSType } from '@/zeus/index.js';
import { extractHusarData } from '@/TailwindPlay/shared/extract.js';
import { evaluateConditionNode, evaluateSetValueRules } from '@/TailwindPlay/shared/rules.js';

export const TailwindPlayOuter: React.FC<
  Omit<TailwindPlayCommonParams, 'f' | 'index'> & {
    fields: FieldFrontType[];
    mainBackground?: string;
    containerBackground?: string;
  }
> = ({ fields, mainBackground, containerBackground, ...commonParams }) => {
  return (
    <div
      className="w-full min-h-screen max-h-full flex items-center justify-center overflow-auto"
      style={{ backgroundColor: mainBackground || 'white' }}
    >
      <div id="background-container" className="w-full" style={{ backgroundColor: containerBackground || 'white' }}>
        <TailwindPlay {...commonParams} fields={fields} />
      </div>
    </div>
  );
};

const shouldShowField = (
  f: FieldFrontType,
  state: Record<string, any>,
  modelData: Record<string, any>,
  prefix: string,
): boolean => {
  if (!f.visual?.rules || f.visual.rules.length === 0) {
    return true;
  }

  const allRulesWithoutActions = f.visual.rules.every((rule) => !rule.actions || rule.actions.length === 0);

  if (allRulesWithoutActions) {
    return f.visual.rules.every((rule) =>
      rule.conditions.every((condition) => evaluateConditionNode(f, condition, state, modelData, prefix)),
    );
  }

  return true;
};

export function TailwindPlay<T extends FieldFrontType>({
  fields,
  ...commonParams
}: Omit<TailwindPlayCommonParams, 'f' | 'index'> & {
  fields: T[];
}) {
  // If rootFields is not set, this is the root level, so use these fields
  const rootFields = commonParams.rootFields || fields;

  const visibleFields = fields
    .filter((f) => !!(f.visual || (!f.visual && f.type !== CMSType.STRING)))
    .filter((f) =>
      shouldShowField(f, commonParams.state || {}, commonParams.modelData || {}, commonParams.prefix || ''),
    );

  return (
    <>
      {visibleFields.map((f) => (
        <React.Fragment key={f.name}>
          <FieldSingle f={f} {...commonParams} rootFields={rootFields} />
        </React.Fragment>
      ))}
    </>
  );
}

export const ListField: React.FC<TailwindPlayCommonParams> = ({ f, index, ...commonProps }) => {
  const { modelData } = commonProps;
  const key = `${commonProps.prefix || ''}${f.name}${typeof index === 'number' ? `[${index}]` : ''}`;
  const values: Array<any> = get(modelData, key) || [];

  if (!Array.isArray(values)) {
    return null;
  }
  return (
    <>
      {values.map((_, i) => (
        <React.Fragment key={i}>
          <FieldSingle {...commonProps} index={i} f={{ ...f, list: false }} />
        </React.Fragment>
      ))}
    </>
  );
};

export const FieldSingle: React.FC<TailwindPlayCommonParams> = (props) => {
  const { f } = props;
  const { v, attributes, overrideKey } = extractHusarData(props);

  const componentOverride = props.components?.[overrideKey]?.({
    data: v,
    attrs: attributes,
    f,
    component: <FieldSingleInner {...props} />,
  });

  return componentOverride || <FieldSingleInner {...props} />;
};

// Helper: Check if field should be hidden based on SET_DISABLED actions
const isFieldHidden = (
  f: FieldFrontType,
  state: Record<string, any>,
  modelData: Record<string, any>,
  prefix: string,
): boolean => {
  const hiddenRules = (f.visual?.rules || []).filter(
    (rule) => rule.actions && rule.actions.some((a) => a.type === ActionType.SET_DISABLED && a.value === 'hidden'),
  );

  return hiddenRules.some((rule) =>
    rule.conditions?.every((condition) => evaluateConditionNode(f, condition, state, modelData, prefix)),
  );
};

export const FieldSingleInner: React.FC<TailwindPlayCommonParams> = (props) => {
  const { f, prefix = '', ...commonParams } = props;
  const { key, attributes, overrideKey } = extractHusarData(props);

  if (isFieldHidden(f, commonParams.state || {}, commonParams.modelData || {}, prefix)) {
    return null;
  }
  if (f.list) {
    return <ListField {...props} />;
  }

  // Helper: Find a field by path in the field tree
  const findFieldPath = (fieldName: string, fields: FieldFrontType[], currentPrefix: string = ''): string | null => {
    for (const field of fields) {
      const fieldPath = currentPrefix ? `${currentPrefix}${field.name}` : field.name;

      if (field.name === fieldName) {
        return fieldPath;
      }

      // Recursively search in nested fields
      if (field.fields && field.fields.length > 0) {
        const found = findFieldPath(fieldName, field.fields as FieldFrontType[], `${fieldPath}.`);
        if (found) {
          return found;
        }
      }
    }

    return null;
  };

  const applyPrefixToPath = (p: string | undefined): string => {
    if (!p || !prefix || p.startsWith(prefix)) {
      return p as string;
    }
    if (commonParams.rootFields) {
      const targetFieldName = p.split('.')[0];
      const foundPath = findFieldPath(targetFieldName, commonParams.rootFields);
      if (foundPath) {
        const pathSegments = p.split('.');
        const result = pathSegments.length > 1 ? `${foundPath}.${pathSegments.slice(1).join('.')}` : foundPath;
        return result;
      }
    }
    const firstSegment = p.split('.')[0];
    const prefixSegments = prefix.split('.').filter(Boolean);
    const prefixFirstSegment = prefixSegments[0];
    const stateKeys = Object.keys(commonParams.state || {});
    if (
      firstSegment !== prefixFirstSegment &&
      (firstSegment in (commonParams.state || {}) || firstSegment in (commonParams.modelData || {}))
    ) {
      return p;
    }
    if (prefixSegments.length > 0) {
      for (let i = 1; i <= prefixSegments.length; i++) {
        const ancestorPrefix = prefixSegments.slice(0, i).join('.');
        const testPath = `${ancestorPrefix}.${p}`;
        if (
          testPath in (commonParams.state || {}) ||
          stateKeys.some((k) => k === testPath || k.startsWith(`${testPath}.`))
        ) {
          return testPath;
        }
      }
    }
    const normPrefix = prefix.endsWith('.') ? prefix.slice(0, -1) : prefix;
    const lastSeg = normPrefix.split('.').filter(Boolean).pop();
    if (lastSeg && p.startsWith(`${lastSeg}.`)) {
      const result = `${prefix}${p.slice(lastSeg.length + 1)}`;
      return result;
    }

    const result = `${prefix}${p}`;
    return result;
  };

  const onChange = (key: string, value: any) => {
    const actionsToApply =
      f.visual?.rules
        ?.filter((rule) => {
          if (!rule.actions || rule.actions.length === 0) return false;

          const conditionsMet = rule.conditions?.every((condition) =>
            evaluateConditionNode(
              f,
              condition,
              { ...commonParams.state, [key]: value },
              commonParams.modelData || {},
              prefix,
            ),
          );
          return conditionsMet;
        })
        .flatMap((rule) => rule.actions)
        .filter((a) => typeof a.path === 'string')
        .map((a) => {
          const prefixedPath = applyPrefixToPath(a.path);
          return { ...a, path: prefixedPath };
        }) || [];

    const uniqueActions = Array.from(
      new Map(actionsToApply.map((a) => [a.path + JSON.stringify(a.value), a])).values(),
    );
    const actions = [...uniqueActions, { path: key, value }];
    const newData = { ...commonParams.state, [key]: value };

    actions.forEach((action) => {
      if (action.value === null || action.value === undefined) {
        delete newData[action.path];
      } else {
        newData[action.path] = action.value;
      }
    });
    commonParams.setState?.(newData);
  };

  const interactive = { ...props, value: commonParams.state?.[key] || '', onChange };
  const nonInteractive = { ...props, rootFields: undefined, host: undefined };

  // Handle nested shapes/objects
  if (f.fields && (f.type === CMSType.SHAPE || f.type === CMSType.OBJECT || f.type === CMSType.OBJECT_TABS)) {
    const openValue = evaluateSetValueRules(
      f,
      ActionType.SET_VALUE,
      commonParams.state || {},
      commonParams.modelData || {},
      prefix,
    );

    const open = openValue === true || openValue === 'true' ? true : false;
    const elementProps = {
      ...attributes,
      ...(open !== undefined ? { open } : {}),
      id: f.name,
      'data-field-name': key,
    };

    const newDataPrefix = `${overrideKey}.`;
    const newPrefix = `${key}.`;
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { index: _i, ...commonParamsRest } = commonParams;
    return createElement(
      f.visual?.component || 'div',
      elementProps,
      <TailwindPlay
        {...commonParamsRest}
        dataPrefix={newDataPrefix}
        prefix={newPrefix}
        fields={f.fields}
        rootFields={commonParams.rootFields}
      />,
    );
  }

  return (
    <>
      {/* CAN BE SSR? */}
      {f.type === CMSType.CONTENT && <FieldContent {...nonInteractive} />}
      {f.type === CMSType.STRING && <FieldString {...nonInteractive} />}
      {f.type === CMSType.NUMBER && <FieldNumber {...nonInteractive} />}
      {f.type === CMSType.SELECT && <FieldSelect {...nonInteractive} />}
      {f.type === CMSType.RELATION && <FieldRelation {...nonInteractive} />}
      {f.type === CMSType.BOOLEAN && <FieldBoolean {...nonInteractive} />}
      {f.type === CMSType.DATE && <FieldDate {...nonInteractive} />}
      {f.type === CMSType.TITLE && <FieldTitle {...nonInteractive} />}
      {f.type === CMSType.IMAGE_URL && <FieldLocalImage {...nonInteractive} />}
      {f.type === CMSType.IMAGE && <FieldS3Image {...nonInteractive} />}
      {f.type === CMSType.VIDEO && <FieldS3Video {...nonInteractive} />}
      {f.type === CMSType.FILE && <FieldFile {...nonInteractive} />}
      {f.type === CMSType.VARIABLE && <FieldVariable {...nonInteractive} />}
      {f.type === CMSType.LINK && <FieldLink {...nonInteractive} />}
      {/* NEED TO BE CSR? */}
      {f.type === CMSType.SCRIPT && <FieldScript {...nonInteractive} />}
      {f.type === CMSType.BUTTON && <FieldButton {...interactive} />}
      {f.type === CMSType.INPUT && <FieldInput {...interactive} />}
      {f.type === CMSType.CHECKBOX && <FieldCheckbox {...interactive} />}
    </>
  );
};
