import { useCallback, useEffect, useRef, useState } from "react";
import { Controller, useForm } from "react-hook-form";

import { Flex, Label, Switch, Text } from "@phoenix/components";
import { BuiltInEvaluatorOutputConfig } from "@phoenix/components/evaluators/BuiltInEvaluatorOutputConfig";
import { useFlattenedEvaluatorInputKeys } from "@phoenix/components/evaluators/EvaluatorInputMapping";
import { RegexEvaluatorCodeBlock } from "@phoenix/components/evaluators/RegexEvaluatorCodeBlock";
import { SwitchableEvaluatorInput } from "@phoenix/components/evaluators/SwitchableEvaluatorInput";
import { RegexField } from "@phoenix/components/RegexField";
import {
  useEvaluatorStore,
  useEvaluatorStoreInstance,
} from "@phoenix/contexts/EvaluatorContext/useEvaluatorStore";

const useRegexEvaluatorForm = () => {
  const store = useEvaluatorStoreInstance();
  const { pathMapping, literalMapping } = useEvaluatorStore(
    (state) => state.evaluator.inputMapping
  );
  const form = useForm({
    defaultValues: { pathMapping, literalMapping },
    mode: "onChange",
  });
  const subscribe = form.subscribe;
  useEffect(() => {
    return subscribe({
      formState: { isValid: true, values: true },
      callback({ values: { pathMapping, literalMapping }, isValid }) {
        if (!isValid) {
          return;
        }
        const { setPathMapping, setLiteralMapping } = store.getState();
        setPathMapping({ ...pathMapping });
        setLiteralMapping({ ...literalMapping });
      },
    });
  }, [subscribe, store]);
  return form;
};

export const RegexEvaluatorForm = () => {
  const form = useRegexEvaluatorForm();
  const { control, getValues, setValue, trigger } = form;
  const store = useEvaluatorStoreInstance();
  const [textPath, setTextPath] = useState<string>(
    () => getValues("pathMapping.text") ?? ""
  );

  // Cache for the latest async regex validation result from RegexField.
  // Read by the validate rule so trigger() returns the correct validity.
  // Initially set to true to avoid error flickering when the user starts typing.
  const regexValidCache = useRef(true);

  // Updates the cache and re-syncs RHF error state via trigger().
  const handleRegexValidationChange = useCallback(
    (valid: boolean) => {
      regexValidCache.current = valid;
      trigger("literalMapping.pattern");
    },
    [trigger]
  );

  const triggerValidation = useCallback(async () => {
    return trigger([
      "literalMapping.pattern",
      "pathMapping.text",
      "literalMapping.text",
    ]);
  }, [trigger]);

  useEffect(() => {
    const unregister = store
      .getState()
      .registerValidator("regexFields", triggerValidation);
    return unregister;
  }, [store, triggerValidation]);

  const evaluatorMappingSource = useEvaluatorStore(
    (state) => state.evaluatorMappingSource
  );
  const allExampleKeys = useFlattenedEvaluatorInputKeys(evaluatorMappingSource);

  // Determine initial mode based on existing values
  const textDefaultMode =
    getValues("literalMapping.text") != null ? "literal" : "path";

  return (
    <Flex direction="column" gap="size-200">
      <Flex direction="column" gap="size-100">
        <Controller
          control={control}
          name="literalMapping.pattern"
          rules={{
            required: "Regex pattern is required",
            validate: () => regexValidCache.current,
          }}
          render={({ field, fieldState: { error } }) => (
            <RegexField
              {...field}
              value={String(field.value ?? "")}
              isInvalid={!!error}
              error={error?.message}
              description="The regex pattern to match against the text. e.g. ^[0-9]+$"
              label="Pattern*"
              placeholder="e.g. ^[0-9]+$"
              onValidationChange={handleRegexValidationChange}
            />
          )}
        />
        <SwitchableEvaluatorInput
          fieldName="text"
          label="Text"
          description="The text to search."
          defaultMode={textDefaultMode}
          control={control}
          setValue={setValue}
          pathOptions={allExampleKeys}
          pathPlaceholder="Map an example field to Text"
          literalPlaceholder="Enter text value"
          pathInputValue={textPath}
          onPathInputChange={setTextPath}
          isRequired
        />
        <Controller
          name="literalMapping.full_match"
          control={control}
          defaultValue={false}
          render={({ field }) => (
            <Switch
              {...field}
              value={String(field.value ?? "")}
              onChange={(value) => field.onChange(value)}
              isSelected={Boolean(
                typeof field.value === "boolean"
                  ? field.value
                  : typeof field.value === "string"
                    ? field.value.toLowerCase() === "true"
                    : false
              )}
            >
              <Label>Full match</Label>
              <Text slot="description">
                If true, pattern must match entire text; if false, searches for
                pattern anywhere.
              </Text>
            </Switch>
          )}
        />
      </Flex>
      <BuiltInEvaluatorOutputConfig />
      <RegexEvaluatorCodeBlock />
    </Flex>
  );
};
