import type { Interpolation, Theme } from "@emotion/react";
import ErrorOutline from "@mui/icons-material/ErrorOutline";
import Button from "@mui/material/Button";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormHelperText from "@mui/material/FormHelperText";
import Radio from "@mui/material/Radio";
import RadioGroup from "@mui/material/RadioGroup";
import TextField, { type TextFieldProps } from "@mui/material/TextField";
import Tooltip from "@mui/material/Tooltip";
import { type FC, type ReactNode, useState } from "react";
import type { TemplateVersionParameter } from "api/typesGenerated";
import { ExternalImage } from "components/ExternalImage/ExternalImage";
import { MemoizedMarkdown } from "components/Markdown/Markdown";
import { Pill } from "components/Pill/Pill";
import { Stack } from "components/Stack/Stack";
import type {
  AutofillBuildParameter,
  AutofillSource,
} from "utils/richParameters";
import { MultiTextField } from "./MultiTextField";

const isBoolean = (parameter: TemplateVersionParameter) => {
  return parameter.type === "bool";
};

const styles = {
  label: {
    marginBottom: 4,
  },
  labelCaption: (theme) => ({
    fontSize: 14,
    color: theme.palette.text.secondary,

    ".small &": {
      fontSize: 13,
      lineHeight: "140%",
    },
  }),
  labelPrimary: (theme) => ({
    fontSize: 16,
    color: theme.palette.text.primary,
    fontWeight: 500,
    display: "flex",
    alignItems: "center",
    flexWrap: "wrap",
    gap: 8,

    "& p": {
      margin: 0,
      lineHeight: "24px", // Keep the same as ParameterInput
    },

    ".small &": {
      fontSize: 14,
    },
  }),
  optionalLabel: (theme) => ({
    fontSize: 14,
    color: theme.palette.text.disabled,
    fontWeight: 500,
  }),
  textField: {
    ".small & .MuiInputBase-root": {
      height: 36,
      fontSize: 14,
      borderRadius: 6,
    },
  },
  radioGroup: {
    ".small & .MuiFormControlLabel-label": {
      fontSize: 14,
    },
    ".small & .MuiRadio-root": {
      padding: "6px 9px", // 8px + 1px border
    },
    ".small & .MuiRadio-root svg": {
      width: 16,
      height: 16,
    },
  },
  checkbox: {
    display: "flex",
    alignItems: "center",
    gap: 8,
  },
  labelIconWrapper: {
    width: 20,
    height: 20,
    display: "block",
    flexShrink: 0,

    ".small &": {
      display: "none",
    },
  },
  labelIcon: {
    width: "100%",
    height: "100%",
    objectFit: "contain",
  },
  optionIcon: {
    pointerEvents: "none",
    maxHeight: 20,
    width: 20,

    ".small &": {
      maxHeight: 16,
      width: 16,
    },
  },
  suggestion: (theme) => ({
    color: theme.roles.info.fill.solid,
    marginLeft: "-4px",
    padding: "4px 6px",
    lineHeight: "inherit",
    fontSize: "inherit",
    height: "unset",
    minWidth: "unset",
  }),
} satisfies Record<string, Interpolation<Theme>>;

export interface ParameterLabelProps {
  parameter: TemplateVersionParameter;
}

const ParameterLabel: FC<ParameterLabelProps> = ({ parameter }) => {
  const hasDescription = parameter.description && parameter.description !== "";
  const displayName = parameter.display_name
    ? parameter.display_name
    : parameter.name;

  const labelPrimary = (
    <span css={styles.labelPrimary}>
      {displayName}

      {!parameter.required && (
        <Tooltip title="If no value is specified, the system will default to the value set by the administrator.">
          <span css={styles.optionalLabel}>(optional)</span>
        </Tooltip>
      )}
      {!parameter.mutable && (
        <Tooltip title="This value cannot be modified after the workspace has been created.">
          <Pill type="warning" icon={<ErrorOutline />}>
            Immutable
          </Pill>
        </Tooltip>
      )}
    </span>
  );

  return (
    <label htmlFor={parameter.name}>
      <Stack direction="row" alignItems="center">
        {parameter.icon && (
          <span css={styles.labelIconWrapper}>
            <ExternalImage
              css={styles.labelIcon}
              alt="Parameter icon"
              src={parameter.icon}
            />
          </span>
        )}

        {hasDescription ? (
          <Stack spacing={0}>
            {labelPrimary}
            <MemoizedMarkdown css={styles.labelCaption}>
              {parameter.description}
            </MemoizedMarkdown>
          </Stack>
        ) : (
          labelPrimary
        )}
      </Stack>
    </label>
  );
};

type Size = "medium" | "small";

export type RichParameterInputProps = Omit<
  TextFieldProps,
  "size" | "onChange"
> & {
  parameter: TemplateVersionParameter;
  parameterAutofill?: AutofillBuildParameter;
  onChange: (value: string) => void;
  size?: Size;
};

const autofillDescription: Partial<Record<AutofillSource, ReactNode>> = {
  url: " from the URL.",
};

export const RichParameterInput: FC<RichParameterInputProps> = ({
  size = "medium",
  parameter,
  parameterAutofill,
  onChange,
  ...fieldProps
}) => {
  const autofillSource = parameterAutofill?.source;
  const autofillValue = parameterAutofill?.value;
  const [hideSuggestion, setHideSuggestion] = useState(false);

  return (
    <Stack
      direction="column"
      spacing={size === "small" ? 1.25 : 2}
      className={size}
      data-testid={`parameter-field-${parameter.name}`}
    >
      <ParameterLabel parameter={parameter} />
      <div css={{ display: "flex", flexDirection: "column" }}>
        <RichParameterField
          {...fieldProps}
          onChange={onChange}
          size={size}
          parameter={parameter}
        />
        {!parameter.ephemeral &&
          autofillSource === "user_history" &&
          autofillValue &&
          !hideSuggestion && (
            <FormHelperText>
              <Button
                variant="text"
                css={styles.suggestion}
                onClick={() => {
                  onChange(autofillValue);
                  setHideSuggestion(true);
                }}
              >
                {autofillValue}
              </Button>{" "}
              was recently used for this parameter.
            </FormHelperText>
          )}
        {autofillSource && autofillDescription[autofillSource] && (
          <div css={{ marginTop: 4, fontSize: 12 }}>
            🪄 Autofilled {autofillDescription[autofillSource]}
          </div>
        )}
      </div>
    </Stack>
  );
};

const RichParameterField: FC<RichParameterInputProps> = ({
  disabled,
  onChange,
  parameter,
  value,
  size,
  ...props
}) => {
  const small = size === "small";

  if (isBoolean(parameter)) {
    return (
      <RadioGroup
        id={parameter.name}
        data-testid="parameter-field-bool"
        css={styles.radioGroup}
        value={value}
        onChange={(_, value) => onChange(value)}
      >
        <FormControlLabel
          disabled={disabled}
          value="true"
          control={<Radio size="small" />}
          label="True"
        />
        <FormControlLabel
          disabled={disabled}
          value="false"
          control={<Radio size="small" />}
          label="False"
        />
      </RadioGroup>
    );
  }

  if (parameter.options.length > 0) {
    return (
      <RadioGroup
        id={parameter.name}
        data-testid="parameter-field-options"
        css={styles.radioGroup}
        value={value}
        onChange={(_, value) => onChange(value)}
      >
        {parameter.options.map((option) => (
          <FormControlLabel
            disabled={disabled}
            key={option.name}
            value={option.value}
            control={<Radio size="small" />}
            label={
              <Stack direction="row" alignItems="center">
                {option.icon && (
                  <ExternalImage
                    css={styles.optionIcon}
                    src={option.icon}
                    alt="Parameter icon"
                  />
                )}
                {option.description ? (
                  <Stack
                    spacing={small ? 1 : 0}
                    alignItems={small ? "center" : undefined}
                    direction={small ? "row" : "column"}
                    css={{ padding: small ? undefined : "4px 0" }}
                  >
                    {small ? (
                      <Tooltip
                        title={
                          <MemoizedMarkdown>
                            {option.description}
                          </MemoizedMarkdown>
                        }
                      >
                        <div>{option.name}</div>
                      </Tooltip>
                    ) : (
                      <>
                        <span>{option.name}</span>
                        <MemoizedMarkdown css={styles.labelCaption}>
                          {option.description}
                        </MemoizedMarkdown>
                      </>
                    )}
                  </Stack>
                ) : (
                  option.name
                )}
              </Stack>
            }
          />
        ))}
      </RadioGroup>
    );
  }

  if (parameter.type === "list(string)") {
    let values: string[] = [];

    if (typeof value !== "string") {
      throw new Error("Expected value to be a string");
    }

    if (value) {
      try {
        values = JSON.parse(value) as string[];
      } catch (e) {
        console.error("Error parsing list(string) parameter", e);
      }
    }

    return (
      <MultiTextField
        id={parameter.name}
        data-testid="parameter-field-list-of-string"
        label={props.label as string}
        values={values}
        onChange={(values) => {
          try {
            const value = JSON.stringify(values);
            onChange(value);
          } catch (e) {
            console.error("Error on change of list(string) parameter", e);
          }
        }}
      />
    );
  }

  // A text field can technically handle all cases!
  // As other cases become more prominent (like filtering for numbers),
  // we should break this out into more finely scoped input fields.
  return (
    <TextField
      {...props}
      id={parameter.name}
      data-testid="parameter-field-text"
      css={styles.textField}
      type={parameter.type}
      disabled={disabled}
      required={parameter.required}
      placeholder={parameter.default_value}
      value={value}
      onChange={(event) => {
        onChange(event.target.value);
      }}
    />
  );
};
