import React, { useCallback, useContext, useEffect, useState } from 'react';
import Select from 'react-select';
import NamespaceHeader from '../../components/NamespaceHeader';
import { DataJunctionAPI } from '../../services/DJService';
import DJClientContext from '../../providers/djclient';
import { useCurrentUser } from '../../providers/UserProvider';
import 'react-querybuilder/dist/query-builder.scss';
import 'styles/styles.scss';
import './styles.css';
import '../QueryPlannerPage/styles.css';
import { ErrorMessage, FastField, Field, Form, Formik, useField } from 'formik';
import { displayMessageAfterSubmit } from '../../../utils/form';
import { useParams, useNavigate } from 'react-router-dom';
import { Action } from '../../components/forms/Action';
import NodeNameField from '../../components/forms/NodeNameField';
import { MetricsSelect } from './MetricsSelect';
import { DimensionsSelect } from './DimensionsSelect';
import { CubePreviewPanel } from './CubePreviewPanel';

// Description textarea using FastField so typing here doesn't trigger
// re-renders of unrelated form sections (heavy ones like the SQL preview).
const DescriptionField = () => (
  <FastField
    as="textarea"
    id="Description"
    name="description"
    placeholder="Describe your cube"
  />
);

// Simple Tags select matching MetricsSelect styling
const CubeTagsSelect = ({ defaultValue }) => {
  const djClient = useContext(DJClientContext).DataJunctionAPI;
  const [field, , helpers] = useField('tags');
  const [options, setOptions] = useState([]);
  const [selected, setSelected] = useState(defaultValue || []);

  useEffect(() => {
    djClient.listTags().then(tags => {
      setOptions(tags.map(t => ({ value: t.name, label: t.display_name })));
    });
  }, [djClient]);

  useEffect(() => {
    if (defaultValue) setSelected(defaultValue);
  }, [defaultValue]);

  const handleChange = sel => {
    setSelected(sel || []);
    helpers.setValue((sel || []).map(s => s.value));
  };

  return (
    <Select
      value={selected}
      options={options}
      onChange={handleChange}
      isMulti
      placeholder="Select tags..."
    />
  );
};

// Simple Owners select matching MetricsSelect styling
const CubeOwnersSelect = ({ defaultValue, isEdit = false }) => {
  const djClient = useContext(DJClientContext).DataJunctionAPI;
  const { currentUser } = useCurrentUser();
  const [field, , helpers] = useField('owners');
  const [options, setOptions] = useState([]);
  const [selected, setSelected] = useState(defaultValue || []);

  useEffect(() => {
    djClient.users().then(users => {
      setOptions(users.map(u => ({ value: u.username, label: u.username })));
    });
  }, [djClient]);

  // Only auto-default to current user in Add mode; in Edit mode wait for defaultValue
  useEffect(() => {
    if (defaultValue) {
      setSelected(defaultValue);
      helpers.setValue(defaultValue.map(d => d.value));
    } else if (!isEdit && currentUser) {
      const def = [
        { value: currentUser.username, label: currentUser.username },
      ];
      setSelected(def);
      helpers.setValue([currentUser.username]);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [defaultValue, currentUser, isEdit]);

  const handleChange = sel => {
    setSelected(sel || []);
    helpers.setValue((sel || []).map(s => s.value));
  };

  return (
    <Select
      value={selected}
      options={options}
      onChange={handleChange}
      isMulti
      placeholder="Select owners..."
    />
  );
};

export function CubeBuilderPage() {
  const djClient = useContext(DJClientContext).DataJunctionAPI;
  const navigate = useNavigate();

  let { nodeType, initialNamespace, name } = useParams();
  const action = name !== undefined ? Action.Edit : Action.Add;
  const validator = ruleType => !!ruleType.value;

  const initialValues = {
    name: action === Action.Edit ? name : '',
    namespace: action === Action.Add ? initialNamespace : '',
    display_name: '',
    description: '',
    mode: 'published',
    metrics: [],
    dimensions: [],
    filters: [],
    tags: [],
    owners: [],
  };

  const handleSubmit = (values, { setSubmitting, setStatus }) => {
    if (action === Action.Add) {
      setTimeout(() => {
        createNode(values, setStatus);
        setSubmitting(false);
      }, 400);
    } else {
      setTimeout(() => {
        patchNode(values, setStatus);
        setSubmitting(false);
      }, 400);
    }
    window.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
  };

  const createNode = async (values, setStatus) => {
    const { status, json } = await djClient.createCube(
      values.name,
      values.display_name,
      values.description,
      values.mode,
      values.metrics,
      values.dimensions,
      values.filters || [],
    );
    if (status === 200 || status === 201) {
      if (values.tags) {
        await djClient.tagsNode(values.name, values.tags);
      }
      setStatus({
        success: true,
        savedName: json.name,
      });
    } else {
      setStatus({
        failure: `${json.message}`,
      });
    }
  };

  const patchNode = async (values, setStatus) => {
    const { status, json } = await djClient.patchCube(
      values.name,
      values.display_name,
      values.description,
      values.mode,
      values.metrics,
      values.dimensions,
      values.filters || [],
      values.owners,
    );
    const tagsResponse = await djClient.tagsNode(
      values.name,
      (values.tags || []).map(tag => tag),
    );
    if ((status === 200 || status === 201) && tagsResponse.status === 200) {
      setStatus({
        success: true,
        savedName: json.name,
      });
    } else {
      setStatus({
        failure: `${json.message}`,
      });
    }
  };

  const updateFieldsWithNodeData = (
    data,
    setFieldValue,
    setSelectTags,
    setSelectOwners,
  ) => {
    setFieldValue('display_name', data.current.displayName || '', false);
    setFieldValue('description', data.current.description || '', false);
    setFieldValue('mode', data.current.mode.toLowerCase() || 'draft', false);
    setFieldValue(
      'tags',
      data.tags.map(tag => tag.name),
    );
    // Store default values as arrays for the Select components
    setSelectTags(
      data.tags.map(t => ({ value: t.name, label: t.displayName })),
    );
    if (data.owners) {
      setSelectOwners(
        data.owners.map(owner => ({
          value: owner.username,
          label: owner.username,
        })),
      );
    }
  };

  // @ts-ignore
  return (
    <>
      <div className="mid">
        <NamespaceHeader
          namespace={
            initialNamespace
              ? initialNamespace
              : name
              ? name.substring(0, name.lastIndexOf('.'))
              : ''
          }
        />
        <Formik
          initialValues={initialValues}
          validate={validator}
          onSubmit={handleSubmit}
        >
          {function Render({
            isSubmitting,
            status,
            setFieldValue,
            values,
            props,
          }) {
            const [node, setNode] = useState([]);
            const [selectTags, setSelectTags] = useState(null);
            const [selectOwners, setSelectOwners] = useState(null);
            const [justSaved, setJustSaved] = useState(false);
            const [saveError, setSaveError] = useState(null);

            // Get cube
            useEffect(() => {
              const fetchData = async () => {
                if (name) {
                  const cube = await djClient.getCubeForEditing(name);
                  setNode(cube);
                  updateFieldsWithNodeData(
                    cube,
                    setFieldValue,
                    setSelectTags,
                    setSelectOwners,
                  );
                }
              };
              fetchData().catch(console.error);
            }, [setFieldValue]);

            // Stable callbacks so the memoized child components don't re-render
            // on every Formik state change (e.g. typing in display_name).
            const setMetrics = useCallback(
              v => setFieldValue('metrics', v),
              [setFieldValue],
            );
            const setDimensions = useCallback(
              v => setFieldValue('dimensions', v),
              [setFieldValue],
            );

            // Briefly show "Saved" state on the button, then redirect to the cube page
            useEffect(() => {
              if (status?.success) {
                setJustSaved(true);
                setSaveError(null);
                const savedName = status.savedName;
                const timer = setTimeout(() => {
                  if (savedName) {
                    navigate(`/nodes/${savedName}`);
                  }
                }, 1200);
                return () => clearTimeout(timer);
              }
              if (status?.failure) {
                setSaveError(status.failure);
                setJustSaved(false);
              }
            }, [status, navigate]);

            return (
              <Form className="cube-builder">
                {/* Header */}
                <div className="cube-builder-header">
                  <h2>
                    {action === Action.Edit ? 'Edit' : 'Create'}{' '}
                    <span className="node_type__cube node_type_creation_heading">
                      Cube
                    </span>
                  </h2>
                </div>

                {/* Two-column layout */}
                <div className="cube-builder-layout">
                  {/* Left: Main form */}
                  <div className="cube-builder-main">
                    {/* Details Section */}
                    <div className="cube-form-section">
                      <div className="cube-form-section-body">
                        {action === Action.Add ? (
                          <>
                            {/* Add mode uses NodeNameField for namespace/name */}
                            <NodeNameField />

                            {/* Row: Description | Mode */}
                            <div className="cube-field-row">
                              <div className="cube-field cube-field-grow">
                                <ErrorMessage
                                  name="description"
                                  component="span"
                                />
                                <label
                                  className="cube-field-label"
                                  htmlFor="Description"
                                >
                                  Description
                                </label>
                                <DescriptionField />
                              </div>
                              <div className="cube-field cube-field-small">
                                <label
                                  className="cube-field-label"
                                  htmlFor="Mode"
                                >
                                  Mode
                                </label>
                                <Field as="select" name="mode" id="Mode">
                                  <option value="draft">Draft</option>
                                  <option value="published">Published</option>
                                </Field>
                              </div>
                            </div>

                            {/* Row: Tags | Owners */}
                            <div className="cube-field-row">
                              <div className="cube-field cube-field-half">
                                <label className="cube-field-label">Tags</label>
                                <CubeTagsSelect />
                              </div>
                              <div className="cube-field cube-field-half">
                                <label className="cube-field-label">
                                  Owners
                                </label>
                                <CubeOwnersSelect />
                              </div>
                            </div>
                          </>
                        ) : (
                          <>
                            {/* Row 1: Name (full width) */}
                            <div className="cube-field">
                              <label className="cube-field-label">Name</label>
                              <div className="cube-field-static">{name}</div>
                            </div>

                            {/* Row 2: Display Name | Mode */}
                            <div className="cube-field-row">
                              <div className="cube-field cube-field-grow">
                                <ErrorMessage
                                  name="display_name"
                                  component="span"
                                />
                                <label
                                  className="cube-field-label"
                                  htmlFor="displayName"
                                >
                                  Display Name
                                </label>
                                <FastField
                                  type="text"
                                  name="display_name"
                                  id="displayName"
                                  placeholder="Human readable name"
                                />
                              </div>
                              <div className="cube-field cube-field-small">
                                <label
                                  className="cube-field-label"
                                  htmlFor="Mode"
                                >
                                  Mode
                                </label>
                                <Field as="select" name="mode" id="Mode">
                                  <option value="draft">Draft</option>
                                  <option value="published">Published</option>
                                </Field>
                              </div>
                            </div>

                            {/* Row 3: Description (full width) */}
                            <div className="cube-field">
                              <ErrorMessage
                                name="description"
                                component="span"
                              />
                              <label
                                className="cube-field-label"
                                htmlFor="Description"
                              >
                                Description
                              </label>
                              <DescriptionField />
                            </div>

                            {/* Row 4: Tags | Owners */}
                            <div className="cube-field-row">
                              <div className="cube-field cube-field-half">
                                <label className="cube-field-label">Tags</label>
                                <CubeTagsSelect defaultValue={selectTags} />
                              </div>
                              <div className="cube-field cube-field-half">
                                <label className="cube-field-label">
                                  Owners
                                </label>
                                <CubeOwnersSelect
                                  defaultValue={selectOwners}
                                  isEdit={true}
                                />
                              </div>
                            </div>
                          </>
                        )}
                      </div>
                    </div>

                    {/* Metrics Section */}
                    <div className="cube-form-section">
                      <div className="cube-form-section-header">
                        <h3>Metrics</h3>
                      </div>
                      <div className="cube-form-section-body">
                        <div data-testid="select-metrics">
                          {action === Action.Edit ? (
                            <MetricsSelect cube={node} onChange={setMetrics} />
                          ) : (
                            <MetricsSelect onChange={setMetrics} />
                          )}
                        </div>
                      </div>
                    </div>

                    {/* Dimensions Section */}
                    <div className="cube-form-section">
                      <div className="cube-form-section-header">
                        <h3>Dimensions</h3>
                      </div>
                      <div className="cube-form-section-body">
                        <div data-testid="select-dimensions">
                          {action === Action.Edit ? (
                            <DimensionsSelect
                              cube={node}
                              metrics={values.metrics}
                              onChange={setDimensions}
                            />
                          ) : (
                            <DimensionsSelect
                              metrics={values.metrics}
                              onChange={setDimensions}
                            />
                          )}
                        </div>
                      </div>
                    </div>
                  </div>

                  {/* Right: Sidebar */}
                  <div className="cube-builder-sidebar">
                    <CubePreviewPanel
                      metrics={values.metrics}
                      dimensions={values.dimensions}
                    />

                    <div className="cube-settings">
                      {saveError && (
                        <div className="save-error-message">{saveError}</div>
                      )}
                      <button
                        type="submit"
                        disabled={isSubmitting || justSaved}
                        aria-label="CreateCube"
                        className={`save-cube-btn${
                          justSaved ? ' save-cube-btn--saved' : ''
                        }${isSubmitting ? ' save-cube-btn--loading' : ''}`}
                      >
                        {isSubmitting ? (
                          <>
                            <span className="save-spinner" aria-hidden="true" />
                            Saving...
                          </>
                        ) : justSaved ? (
                          '✓ Saved'
                        ) : action === Action.Add ? (
                          'Create Cube'
                        ) : (
                          'Save'
                        )}
                      </button>
                    </div>
                  </div>
                </div>
              </Form>
            );
          }}
        </Formik>
      </div>
    </>
  );
}

CubeBuilderPage.defaultProps = {
  djClient: DataJunctionAPI,
};
