import { useState } from 'react';

import { selectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { Button, Checkbox, TextArea, Stack, Alert, Box, Field } from '@grafana/ui';
import { type SaveDashboardOptions } from 'app/features/dashboard/components/SaveDashboard/types';

import { type DashboardScene } from '../scene/DashboardScene';

import { type SaveDashboardDrawer } from './SaveDashboardDrawer';
import {
  type DashboardChangeInfo,
  NameAlreadyExistsError,
  SaveButton,
  isNameExistsError,
  isPluginDashboardError,
  isVersionMismatchError,
} from './shared';
import { useSaveDashboard } from './useSaveDashboard';

export interface Props {
  dashboard: DashboardScene;
  drawer: SaveDashboardDrawer;
  changeInfo: DashboardChangeInfo;
}

export function SaveDashboardForm({ dashboard, drawer, changeInfo }: Props) {
  const { hasChanges, hasMigratedToV2, changedSaveModel } = changeInfo;

  const { state, onSaveDashboard } = useSaveDashboard(false);
  const [options, setOptions] = useState<SaveDashboardOptions>({
    folderUid: dashboard.state.meta.folderUid,
    // we need to set the uid here in order to save the dashboard
    // in schema v2 we don't have the uid in the spec
    k8s: {
      ...dashboard.serializer.getK8SMetadata(),
    },
  });

  const onSave = async (overwrite: boolean) => {
    const result = await onSaveDashboard(dashboard, { ...options, rawDashboardJSON: changedSaveModel, overwrite });
    if (result.status === 'success') {
      dashboard.closeModal();
      drawer.state.onSaveSuccess?.();
    }
  };

  const cancelButton = (
    <Button variant="secondary" onClick={() => dashboard.closeModal()} fill="outline">
      <Trans i18nKey="dashboard-scene.save-dashboard-form.cancel-button.cancel">Cancel</Trans>
    </Button>
  );

  const saveButton = (overwrite: boolean) => (
    <SaveButton isValid={hasChanges} isLoading={state.loading} onSave={onSave} overwrite={overwrite} />
  );

  const isMessageTooLongError = (message?: string) => {
    return message && message.length > 500;
  };

  function renderFooter(error?: Error) {
    if (isMessageTooLongError(options.message)) {
      const messageLength = options.message?.length ?? 0;

      return (
        <Alert title={t('save-dashboards.message-length.title', 'Message too long')} severity="error">
          <p>
            <Trans i18nKey="save-dashboards.message-length.info">
              The message is {{ messageLength }} characters, which exceeds the maximum length of 500 characters. Please
              shorten it before saving.
            </Trans>
          </p>
        </Alert>
      );
    }

    if (isVersionMismatchError(error)) {
      return (
        <Alert
          title={t(
            'dashboard-scene.save-dashboard-form.render-footer.title-someone-else-has-updated-this-dashboard',
            'Someone else has updated this dashboard'
          )}
          severity="error"
        >
          <p>
            <Trans i18nKey="dashboard-scene.save-dashboard-form.render-footer.would-still-dashboard">
              Would you still like to save this dashboard?
            </Trans>
          </p>
          <Box paddingTop={2}>
            <Stack alignItems="center">
              {cancelButton}
              {saveButton(true)}
            </Stack>
          </Box>
        </Alert>
      );
    }

    if (isNameExistsError(error)) {
      return <NameAlreadyExistsError />;
    }

    if (isPluginDashboardError(error)) {
      return (
        <Alert
          title={t('dashboard-scene.save-dashboard-form.render-footer.title-plugin-dashboard', 'Plugin dashboard')}
          severity="error"
        >
          <p>
            <Trans i18nKey="dashboard-scene.save-dashboard-form.render-footer.body-plugin-dashboard">
              Your changes will be lost when you update the plugin. Use <strong>Save as</strong> to create custom
              version.
            </Trans>
          </p>
          <Box paddingTop={2}>
            <Stack alignItems="center">
              {cancelButton}
              {saveButton(true)}
            </Stack>
          </Box>
        </Alert>
      );
    }

    return (
      <>
        {error && (
          <Alert
            title={t(
              'dashboard-scene.save-dashboard-form.render-footer.title-failed-to-save-dashboard',
              'Failed to save dashboard'
            )}
            severity="error"
          >
            <p>{error.message}</p>
          </Alert>
        )}
        <Stack alignItems="center">
          {cancelButton}
          {saveButton(false)}
          {!hasChanges && (
            <div>
              <Trans i18nKey="dashboard-scene.save-dashboard-form.render-footer.no-changes-to-save">
                No changes to save
              </Trans>
            </div>
          )}
        </Stack>
      </>
    );
  }

  return (
    <Stack gap={2} direction="column">
      <SaveDashboardFormCommonOptions drawer={drawer} changeInfo={changeInfo} />
      {hasMigratedToV2 && (
        <Alert
          title={t(
            'dashboard-scene.save-dashboard-form.title-dashboard-drastically-changed',
            'Dashboard irreversibly changed'
          )}
          severity="warning"
        >
          <p>
            <Trans i18nKey="dashboard-scene.save-dashboard-form.body-dashboard-drastically-changed">
              The dashboard will be saved using the new experimental Grafana dashboard schema. This action can’t be
              reverted and could result in the irreversible loss of data. We recommend that you save this dashboard as a
              copy instead. If you’re seeing this message in a production environment, contact Support to have the
              feature disabled.
            </Trans>
          </p>
        </Alert>
      )}
      <Field label={t('dashboard-scene.save-dashboard-form.label-message', 'Message')}>
        <TextArea
          aria-label={t('dashboard-scene.save-dashboard-form.aria-label-message', 'message')}
          value={options.message ?? ''}
          onChange={(e) => {
            setOptions({
              ...options,
              message: e.currentTarget.value,
            });
          }}
          placeholder={t(
            'dashboard-scene.save-dashboard-form.placeholder-describe-changes-optional',
            'Add a note to describe your changes (optional).'
          )}
          autoFocus
          rows={5}
        />
      </Field>
      {renderFooter(state.error)}
    </Stack>
  );
}

export interface SaveDashboardFormCommonOptionsProps {
  drawer: SaveDashboardDrawer;
  changeInfo: DashboardChangeInfo;
}

export function SaveDashboardFormCommonOptions({ drawer, changeInfo }: SaveDashboardFormCommonOptionsProps) {
  const {
    saveVariables = false,
    saveTimeRange = false,
    saveRefresh = false,
    showVariablesWarning = false,
  } = drawer.useState();
  const { hasTimeChanges, hasVariableValueChanges, hasRefreshChange } = changeInfo;

  return (
    <Stack direction={'column'} alignItems={'flex-start'}>
      {hasTimeChanges && (
        <Checkbox
          id="save-timerange"
          checked={saveTimeRange}
          onChange={drawer.onToggleSaveTimeRange}
          label={t(
            'dashboard-scene.save-dashboard-form-common-options.save-timerange-label-update-default-time-range',
            'Update default time range'
          )}
          description={t(
            'dashboard-scene.save-dashboard-form-common-options.save-timerange-description-current-range-default',
            'Will make current time range the new default'
          )}
          data-testid={selectors.pages.SaveDashboardModal.saveTimerange}
        />
      )}
      {hasRefreshChange && (
        <Checkbox
          id="save-refresh"
          label={t(
            'dashboard-scene.save-dashboard-form-common-options.save-refresh-label-update-default-refresh-value',
            'Update default refresh value'
          )}
          description={t(
            'dashboard-scene.save-dashboard-form-common-options.save-refresh-description-current-refresh-default',
            'Will make the current refresh the new default'
          )}
          checked={saveRefresh}
          onChange={drawer.onToggleSaveRefresh}
          data-testid={selectors.pages.SaveDashboardModal.saveRefresh}
        />
      )}
      {hasVariableValueChanges && (
        <>
          <Checkbox
            id="save-variables"
            label={t(
              'dashboard-scene.save-dashboard-form-common-options.save-variables-label-update-default-variable-values',
              'Update default variable values'
            )}
            description={t(
              'dashboard-scene.save-dashboard-form-common-options.save-variables-description-current-values-default',
              'Will make the current values the new default'
            )}
            checked={saveVariables}
            onChange={drawer.onToggleSaveVariables}
            data-testid={selectors.pages.SaveDashboardModal.saveVariables}
          />
          {saveVariables && showVariablesWarning && (
            <Alert
              data-testid={selectors.pages.SaveDashboardModal.variablesWarningAlert}
              title={t(
                'dashboard-scene.save-dashboard-form-common-options.show-variables-warning-alert-title',
                'Variable queries failed'
              )}
              severity="warning"
            >
              <Trans i18nKey="dashboard-scene.save-dashboard-form-common-options.show-variables-warning-alert-body">
                Some variables failed to load. If you keep “Update default variable values” checked, the current
                (failed) values will become the dashboard defaults. You can save anyway or uncheck the option to avoid
                storing those (failed) values.
              </Trans>
            </Alert>
          )}
        </>
      )}
    </Stack>
  );
}
