import { Button, Input, MessageBar, MessageBarBody, tokens } from "@fluentui/react-components";
import { Add24Regular, Delete24Regular } from "@fluentui/react-icons";
import { useEffect, useMemo, useState } from "react";
import { useConfigStyles } from "./configStyles";

interface KVEditorProps {
  entries: Record<string, string>;
  onChange: (next: Record<string, string>) => void;
}

export function KVEditor({ entries, onChange }: KVEditorProps): React.JSX.Element {
  const styles = useConfigStyles();
  const rows = useMemo(() => Object.entries(entries), [entries]);
  const entryKeys = useMemo(() => Object.keys(entries), [entries]);
  const entryKeyList = entryKeys.join(",");
  const [editedKeys, setEditedKeys] = useState<Record<string, string>>(
    Object.fromEntries(rows.map(([key]) => [key, key])),
  );
  const [errors, setErrors] = useState<Record<string, string>>({});

  useEffect(() => {
    setEditedKeys(Object.fromEntries(rows.map(([key]) => [key, key])));
    setErrors((previousErrors) =>
      Object.fromEntries(
        entryKeys
          .filter((key) => previousErrors[key])
          .map((key) => [key, previousErrors[key] ?? ""]),
      ),
    );
  }, [entryKeyList, rows, entryKeys]);

  const validateKey = (oldKey: string, newKey: string): string | null => {
    const trimmedKey = newKey.trim();
    if (trimmedKey.length === 0) {
      return "Key may not be blank.";
    }

    const conflict = Object.entries(editedKeys).some(
      ([otherKey, otherValue]) => otherKey !== oldKey && otherValue === trimmedKey,
    );
    if (conflict) {
      return "A setting with this key already exists.";
    }

    return null;
  };

  const updateKeyValue = (oldKey: string, newKey: string): void => {
    setEditedKeys((previous) => ({ ...previous, [oldKey]: newKey }));
    setErrors((previous) =>
      Object.fromEntries(Object.entries(previous).filter(([key]) => key !== oldKey)),
    );
  };

  const commitKeyChange = (oldKey: string): void => {
    const newKey = editedKeys[oldKey] ?? oldKey;
    const error = validateKey(oldKey, newKey);
    if (error) {
      setErrors((previous) => ({ ...previous, [oldKey]: error }));
      return;
    }

    if (newKey !== oldKey) {
      const next: Record<string, string> = {};
      for (const [key, value] of Object.entries(entries)) {
        next[key === oldKey ? newKey : key] = value;
      }
      onChange(next);
    }
  };

  const handleValueChange = (key: string, value: string): void => {
    onChange({ ...entries, [key]: value });
  };

  const handleDelete = (key: string): void => {
    const { [key]: _removed, ...rest } = entries;
    onChange(rest);
  };

  const handleAdd = (): void => {
    let newKey = "key";
    let n = 1;
    while (newKey in entries) {
      newKey = `key${String(n)}`;
      n += 1;
    }
    onChange({ ...entries, [newKey]: "" });
  };

  return (
    <div>
      {rows.map(([key, value]) => (
        <div key={key} className={styles.fieldRow}>
          <Input
            value={editedKeys[key] ?? key}
            size="small"
            style={{ width: 160, fontFamily: tokens.fontFamilyMonospace }}
            aria-label={`Setting name: ${key}`}
            aria-invalid={Boolean(errors[key])}
            onBlur={() => { commitKeyChange(key); }}
            onChange={(_e, d) => { updateKeyValue(key, d.value); }}
          />
          <Input
            value={value}
            size="small"
            style={{ flex: 1, fontFamily: tokens.fontFamilyMonospace }}
            aria-label={`Value for ${key}`}
            onChange={(_e, d) => { handleValueChange(key, d.value); }}
          />
          <Button
            icon={<Delete24Regular />}
            size="small"
            appearance="subtle"
            onClick={() => { handleDelete(key); }}
            aria-label={`Delete key ${key}`}
          />
          {errors[key] ? (
            <MessageBar intent="error" className={styles.fieldError}>
              <MessageBarBody>{errors[key]}</MessageBarBody>
            </MessageBar>
          ) : null}
        </div>
      ))}
      <Button
        icon={<Add24Regular />}
        size="small"
        appearance="outline"
        onClick={handleAdd}
        style={{ marginTop: 4 }}
      >
        Add setting
      </Button>
    </div>
  );
}
