import { InitialEntry } from 'history/createMemoryHistory';
import * as React from 'react';
import { Route, Routes } from 'react-router-dom-v5-compat';
import { Props } from 'react-virtualized-auto-sizer';
import { render, screen, waitFor, within } from 'test/test-utils';
import { byLabelText, byRole } from 'testing-library-selector';

import { CodeEditor } from '@grafana/ui';
import { AppNotificationList } from 'app/core/components/AppNotifications/AppNotificationList';
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
import { testWithFeatureToggles } from 'app/features/alerting/unified/test/test-utils';
import { AccessControlAction } from 'app/types';

import Templates from './Templates';
import { grantUserPermissions } from './mocks';

type CodeEditorProps = React.ComponentProps<typeof CodeEditor>;

jest.mock('app/core/components/AppChrome/AppChromeUpdate', () => ({
  AppChromeUpdate: ({ actions }: { actions: React.ReactNode }) => <div>{actions}</div>,
}));

//mock updateDefinesWithUniqueValue
jest.mock('app/features/alerting/unified/utils/templates', () => ({
  ...jest.requireActual('app/features/alerting/unified/utils/templates'),
  updateDefinesWithUniqueValue: (templateContent: string) => {
    return templateContent.replace(/custom-email/g, 'custom-email_NEW');
  },
}));

jest.mock(
  'react-virtualized-auto-sizer',
  () =>
    ({ children }: Props) =>
      children({ width: 1000, height: 1000, scaledWidth: 1, scaledHeight: 1 })
);

jest.mock('@grafana/ui', () => ({
  ...jest.requireActual('@grafana/ui'),
  CodeEditor: ({ value, onChange }: CodeEditorProps) => (
    <textarea data-testid="code-editor" value={value} onChange={(e) => onChange?.(e.target.value)} />
  ),
}));

const ui = {
  templateForm: byRole('form', { name: 'Template form' }),
  form: {
    title: byLabelText(/Template group name/),
    saveButton: byRole('button', { name: 'Save' }),
  },
};

const navUrl = {
  edit: (name: string) => `/alerting/notifications/templates/${name}/edit?alertmanager=grafana`,
  duplicate: (name: string) => `/alerting/notifications/templates/${name}/duplicate?alertmanager=grafana`,
  new: `/alerting/notifications/templates/new`,
};

setupMswServer();

beforeEach(() => {
  grantUserPermissions([AccessControlAction.AlertingNotificationsRead, AccessControlAction.AlertingNotificationsWrite]);
});

const setup = (initialEntries: InitialEntry[]) => {
  return render(
    <>
      <AppNotificationList />
      <Routes>
        <Route path="/alerting/notifications/templates/*" element={<Templates />} />
      </Routes>
    </>,
    {
      historyOptions: { initialEntries },
    }
  );
};

describe('Templates routes', () => {
  it('allows duplication of template with spaces in name', async () => {
    setup([navUrl.duplicate('template%20with%20spaces')]);

    expect(await screen.findByText('Edit payload')).toBeInTheDocument();
  });

  it('allows editing of template with spaces in name', async () => {
    setup([navUrl.edit('template%20with%20spaces')]);

    expect(await screen.findByText('Edit payload')).toBeInTheDocument();
  });

  it('renders empty template form', async () => {
    setup([navUrl.new]);

    const form = await ui.templateForm.find();

    expect(form).toBeInTheDocument();
    expect(within(form).getByRole('textbox', { name: /Template group name/ })).toHaveValue('');
  });

  it('should pass name validation when editing existing template', async () => {
    const { user } = setup([navUrl.edit('custom-email')]);

    const titleElement = await ui.form.title.find();
    await waitFor(() => {
      expect(titleElement).toHaveValue('custom-email');
    });

    await user.click(ui.form.saveButton.get());

    // No error message should be displayed for a unique name
    expect(screen.queryByText('Another template with this name already exists')).not.toBeInTheDocument();
    await waitFor(() => {
      expect(screen.getByText('Template saved')).toBeInTheDocument();
    });
  });

  it('should display error message when creating new template with duplicate name', async () => {
    const { user } = setup([navUrl.new]);

    const titleElement = await ui.form.title.find();
    await user.type(titleElement, 'custom-email');

    await user.click(ui.form.saveButton.get());

    expect(screen.getByText('Another template with this name already exists')).toBeInTheDocument();
  });
});

describe('Templates K8s API', () => {
  testWithFeatureToggles(['alertingApiServer']);

  it('form edit renders with correct form values', async () => {
    setup([navUrl.edit('k8s-custom-email-resource-name')]);

    const form = await ui.templateForm.find();

    expect(form).toBeInTheDocument();
    expect(within(form).getByRole('textbox', { name: /Template group name/ })).toHaveValue('custom-email');
    expect(within(form).getAllByTestId('code-editor')[0]).toHaveValue(
      '{{ define "custom-email" }}  Custom email template {{ end }}'
    );
  });

  it('renders duplicate template form with correct values', async () => {
    setup([navUrl.duplicate('k8s-custom-email-resource-name')]);

    const form = await ui.templateForm.find();

    expect(form).toBeInTheDocument();
    expect(within(form).getByRole('textbox', { name: /Template group name/ })).toHaveValue('custom-email (copy)');
    expect(within(form).getAllByTestId('code-editor')[0]).toHaveTextContent(
      '{{ define "custom-email_NEW" }} Custom email template {{ end }}'
    );
  });

  it('updates a template', async () => {
    const { user } = setup([navUrl.edit('k8s-custom-email-resource-name')]);

    const form = await ui.templateForm.find();

    const templateEditor = within(form).getAllByTestId('code-editor')[0];

    await user.clear(templateEditor);
    await user.type(templateEditor, '{{ define "custom-email" }}Updated custom email template{{ end }}');

    await user.click(screen.getByRole('button', { name: 'Save' }));

    expect(await screen.findByRole('status', { name: 'Template saved' })).toHaveTextContent(
      'Template custom-email has been saved'
    );

    expect(ui.templateForm.query()).not.toBeInTheDocument();
  });
});
