import { renderHook, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { first, noop } from 'lodash';
import type { JSX } from 'react';
import { Route, Routes } from 'react-router-dom-v5-compat';
import { render } from 'test/test-utils';

import { contextSrv } from 'app/core/services/context_srv';
import { type ReceiversState } from 'app/features/alerting/unified/types/alerting';
import {
  type AlertmanagerGroup,
  MatcherOperator,
  type ObjectMatcher,
  ROUTES_META_SYMBOL,
  type RouteWithID,
} from 'app/plugins/datasource/alertmanager/types';

import { useAlertmanagerAbilities } from '../../hooks/useAbilities';
import { mockReceiversState } from '../../mocks';
import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
import { KnownProvenance } from '../../types/knownProvenance';
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
import { K8sAnnotations } from '../../utils/k8s/constants';

import {
  AUTOGENERATED_ROOT_LABEL_NAME,
  Policy,
  TimingOptionsMeta,
  isAutoGeneratedRoot,
  useCreateDropdownMenuActions,
} from './Policy';

jest.mock('../../hooks/useAbilities', () => ({
  ...jest.requireActual('../../hooks/useAbilities'),
  useAlertmanagerAbilities: jest.fn(),
}));

const useAlertmanagerAbilitiesMock = jest.mocked(useAlertmanagerAbilities);

describe('Policy', () => {
  beforeAll(() => {
    jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
    useAlertmanagerAbilitiesMock.mockReturnValue([
      [true, true],
      [true, true],
      [true, true],
    ]);
  });

  it('should render a policy tree', async () => {
    const onEditPolicy = jest.fn();
    const onAddPolicy = jest.fn();
    const onDeletePolicy = jest.fn();
    const onShowAlertInstances = jest.fn(
      (alertGroups: AlertmanagerGroup[], matchers?: ObjectMatcher[] | undefined) => {}
    );

    const routeTree = mockRoutes;
    const user = userEvent.setup();

    renderPolicy(
      <Policy
        receivers={[{ name: 'grafana-default-email' }]}
        isDefaultPolicy
        currentRoute={routeTree}
        alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
        onEditPolicy={onEditPolicy}
        onAddPolicy={onAddPolicy}
        onDeletePolicy={onDeletePolicy}
        onShowAlertInstances={onShowAlertInstances}
      />
    );

    // should have default policy
    const defaultPolicy = screen.getByTestId('am-root-route-container');
    expect(defaultPolicy).toBeInTheDocument();
    expect(within(defaultPolicy).getByText('Default policy')).toBeVisible();

    // click "more actions" and check if we can edit and delete
    expect(within(defaultPolicy).getByTestId('more-actions')).toBeInTheDocument();
    await user.click(within(defaultPolicy).getByTestId('more-actions'));

    // should be editable
    const editDefaultPolicy = screen.getByRole('menuitem', { name: 'Edit' });
    expect(editDefaultPolicy).toBeInTheDocument();
    expect(editDefaultPolicy).toBeEnabled();
    await user.click(editDefaultPolicy);
    expect(onEditPolicy).toHaveBeenCalledWith(routeTree, true);

    // should not be deletable
    expect(screen.queryByRole('menuitem', { name: 'Delete' })).not.toBeInTheDocument();

    // default policy should show the metadata

    // no continue matching
    expect(within(defaultPolicy).queryByTestId('continue-matching')).not.toBeInTheDocument();

    // for matching instances
    // expect(within(defaultPolicy).getByTestId('matching-instances')).toHaveTextContent('0instances');

    // for contact point
    expect(within(defaultPolicy).getByTestId('contact-point')).toHaveTextContent('grafana-default-email');
    expect(within(defaultPolicy).getByRole('link', { name: 'grafana-default-email' })).toBeInTheDocument();

    // for grouping
    expect(within(defaultPolicy).getByTestId('grouping')).toHaveTextContent('grafana_folder, alertname');

    // no timings
    expect(within(defaultPolicy).queryByTestId('mute-timings')).not.toBeInTheDocument();
    expect(within(defaultPolicy).queryByTestId('active-timings')).not.toBeInTheDocument();

    // for timing options
    expect(within(defaultPolicy).getByTestId('timing-options')).toHaveTextContent(
      'Wait 30s to group instances · Wait 5m before sending updates · Repeated every 4h'
    );

    // should have custom policies (1 direct child + 2 grandchildren, all expanded by default)
    const customPolicies = screen.getAllByTestId('am-route-container');
    expect(customPolicies).toHaveLength(3);

    // all policies should be editable and deletable
    for (const container of customPolicies) {
      const policy = within(container);

      // click "more actions" and check if we can delete
      await user.click(policy.getByTestId('more-actions'));
      expect(screen.queryByRole('menuitem', { name: 'Edit' })).toBeEnabled();
      expect(screen.queryByRole('menuitem', { name: 'Delete' })).toBeEnabled();

      await user.click(screen.getByRole('menuitem', { name: 'Delete' }));
      expect(onDeletePolicy).toHaveBeenCalled();
    }

    // first custom policy should have the correct information
    const firstPolicy = customPolicies[0];
    expect(within(firstPolicy).getByTestId('label-matchers')).toHaveTextContent(/^team \= operations$/);
    expect(within(firstPolicy).getByTestId('continue-matching')).toBeInTheDocument();
    // expect(within(firstPolicy).getByTestId('matching-instances')).toHaveTextContent('0instances');
    expect(within(firstPolicy).getByTestId('contact-point')).toHaveTextContent('provisioned-contact-point');
    expect(within(firstPolicy).getByTestId('mute-timings')).toHaveTextContent('Muted when mt-1');
    expect(within(firstPolicy).getByTestId('active-timings')).toHaveTextContent('Active when mt-2');
    expect(within(firstPolicy).getByTestId('inherited-properties')).toHaveTextContent('Inherited4 properties');

    // second custom policy should be correct
    const secondPolicy = customPolicies[1];
    expect(within(secondPolicy).getByTestId('label-matchers')).toHaveTextContent(/^region \= EMEA$/);
    expect(within(secondPolicy).queryByTestId('continue-matching')).not.toBeInTheDocument();
    expect(within(secondPolicy).queryByTestId('mute-timings')).not.toBeInTheDocument();
    expect(within(secondPolicy).queryByTestId('active-timings')).not.toBeInTheDocument();
    expect(within(secondPolicy).getByTestId('inherited-properties')).toHaveTextContent('Inherited5 properties');

    // third custom policy should be correct
    const thirdPolicy = customPolicies[2];
    expect(within(thirdPolicy).getByTestId('label-matchers')).toHaveTextContent(
      /^foo = barbar = bazbaz = quxasdf = asdftype = diskand 1 more$/
    );
  });

  it('should show export option when export is allowed and supported returns true', async () => {
    const onEditPolicy = jest.fn();
    const onAddPolicy = jest.fn();
    const onDeletePolicy = jest.fn();
    const onShowAlertInstances = jest.fn(
      (alertGroups: AlertmanagerGroup[], matchers?: ObjectMatcher[] | undefined) => {}
    );

    const routeTree = mockRoutes;
    const user = userEvent.setup();

    renderPolicy(
      <Policy
        isDefaultPolicy
        currentRoute={routeTree}
        alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
        onEditPolicy={onEditPolicy}
        onAddPolicy={onAddPolicy}
        onDeletePolicy={onDeletePolicy}
        onShowAlertInstances={onShowAlertInstances}
        isAutoGenerated={false}
      />
    );
    // should have default policy
    const defaultPolicy = screen.getByTestId('am-root-route-container');
    // click "more actions"
    expect(within(defaultPolicy).getByTestId('more-actions')).toBeInTheDocument();
    await user.click(within(defaultPolicy).getByTestId('more-actions'));
    expect(screen.getByRole('menuitem', { name: 'Export' })).toBeInTheDocument();
  });

  it('should not show export option when is not supported', async () => {
    const onEditPolicy = jest.fn();
    const onAddPolicy = jest.fn();
    const onDeletePolicy = jest.fn();
    const onShowAlertInstances = jest.fn(
      (alertGroups: AlertmanagerGroup[], matchers?: ObjectMatcher[] | undefined) => {}
    );

    const routeTree = mockRoutes;

    useAlertmanagerAbilitiesMock.mockReturnValue([
      [true, true],
      [true, true],
      [false, true],
    ]);

    const user = userEvent.setup();

    renderPolicy(
      <Policy
        isDefaultPolicy
        currentRoute={routeTree}
        alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
        onEditPolicy={onEditPolicy}
        onAddPolicy={onAddPolicy}
        onDeletePolicy={onDeletePolicy}
        onShowAlertInstances={onShowAlertInstances}
      />
    );
    // should have default policy
    const defaultPolicy = screen.getByTestId('am-root-route-container');
    // click "more actions"
    expect(within(defaultPolicy).getByTestId('more-actions')).toBeInTheDocument();
    await user.click(within(defaultPolicy).getByTestId('more-actions'));
    expect(screen.queryByRole('menuitem', { name: 'Export' })).not.toBeInTheDocument();
  });

  it('should not show export option when is not allowed', async () => {
    const onEditPolicy = jest.fn();
    const onAddPolicy = jest.fn();
    const onDeletePolicy = jest.fn();
    const onShowAlertInstances = jest.fn(
      (alertGroups: AlertmanagerGroup[], matchers?: ObjectMatcher[] | undefined) => {}
    );

    const routeTree = mockRoutes;

    useAlertmanagerAbilitiesMock.mockReturnValue([
      [true, true],
      [true, true],
      [true, false],
    ]);

    const user = userEvent.setup();

    renderPolicy(
      <Policy
        isDefaultPolicy
        currentRoute={routeTree}
        alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
        onEditPolicy={onEditPolicy}
        onAddPolicy={onAddPolicy}
        onDeletePolicy={onDeletePolicy}
        onShowAlertInstances={onShowAlertInstances}
      />
    );
    // should have default policy
    const defaultPolicy = screen.getByTestId('am-root-route-container');
    // click "more actions"
    expect(within(defaultPolicy).getByTestId('more-actions')).toBeInTheDocument();
    await user.click(within(defaultPolicy).getByTestId('more-actions'));
    expect(screen.queryByRole('menuitem', { name: 'Export' })).not.toBeInTheDocument();
  });

  it('should not allow editing readOnly policy tree', () => {
    const routeTree: RouteWithID = { id: '0', routes: [{ id: '1' }] };

    renderPolicy(
      <Policy
        readOnly
        currentRoute={routeTree}
        alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
        onEditPolicy={noop}
        onAddPolicy={noop}
        onDeletePolicy={noop}
        onShowAlertInstances={noop}
      />
    );

    expect(screen.queryByRole('button', { name: 'Edit' })).not.toBeInTheDocument();
  });

  it.skip('should show matching instances', () => {
    const routeTree: RouteWithID = {
      id: '0',
      routes: [{ id: '1', object_matchers: [['foo', eq, 'bar']] }],
    };

    renderPolicy(
      <Policy
        readOnly
        isDefaultPolicy
        currentRoute={routeTree}
        alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
        onEditPolicy={noop}
        onAddPolicy={noop}
        onDeletePolicy={noop}
        onShowAlertInstances={noop}
      />
    );

    const defaultPolicy = screen.getByTestId('am-root-route-container');
    expect(within(defaultPolicy).getByTestId('matching-instances')).toHaveTextContent('1instance');
    const customPolicy = screen.getByTestId('am-route-container');
    expect(within(customPolicy).getByTestId('matching-instances')).toHaveTextContent('2instances');
  });

  it('should show warnings and errors', () => {
    const routeTree: RouteWithID = {
      id: '0', // this one should show an error
      receiver: 'broken-receiver',
      routes: [{ id: '1', object_matchers: [] }], // this one should show a warning
    };

    const receiversState: ReceiversState = mockReceiversState();

    renderPolicy(
      <Policy
        readOnly
        isDefaultPolicy
        currentRoute={routeTree}
        contactPointsState={receiversState}
        alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
        onEditPolicy={noop}
        onAddPolicy={noop}
        onDeletePolicy={noop}
        onShowAlertInstances={noop}
      />
    );

    const defaultPolicy = screen.getByTestId('am-root-route-container');
    expect(within(defaultPolicy).queryByTestId('matches-all')).not.toBeInTheDocument();
    // Errors are now shown as an icon in the gutter
    expect(within(defaultPolicy).getByTestId('policy-errors')).toBeInTheDocument();

    const customPolicy = screen.getByTestId('am-route-container');
    expect(within(customPolicy).getByTestId('matches-all')).toBeInTheDocument();
  });

  it('shows correct badge when policy has file provenance', () => {
    const mockRoute: RouteWithID = {
      id: 'test-route',
      receiver: 'test-receiver',
      routes: [],
    };

    renderPolicy(
      <Policy
        readOnly
        isDefaultPolicy
        currentRoute={mockRoute}
        contactPointsState={mockReceiversState()}
        alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
        onEditPolicy={noop}
        onAddPolicy={noop}
        onDeletePolicy={noop}
        onShowAlertInstances={noop}
        provenance={KnownProvenance.File}
      />
    );

    const badge = screen.getByText('Provisioned');
    expect(badge).toBeInTheDocument();
  });

  it('shows correct badge when policy has converted_prometheus provenance', () => {
    const mockRoute: RouteWithID = {
      id: 'test-route',
      receiver: 'test-receiver',
      routes: [],
    };

    renderPolicy(
      <Policy
        readOnly
        isDefaultPolicy
        currentRoute={mockRoute}
        contactPointsState={mockReceiversState()}
        alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
        onEditPolicy={noop}
        onAddPolicy={noop}
        onDeletePolicy={noop}
        onShowAlertInstances={noop}
        provenance={KnownProvenance.ConvertedPrometheus}
      />
    );

    const badge = screen.getByText('Imported');
    expect(badge).toBeInTheDocument();
  });

  it('should show manage permissions menu item when route has canAdmin annotation', async () => {
    const routeWithAdmin: RouteWithID = {
      id: '0',
      receiver: 'grafana-default-email',
      group_by: ['grafana_folder', 'alertname'],
      routes: [],
      group_wait: '30s',
      group_interval: '5m',
      repeat_interval: '4h',
      [ROUTES_META_SYMBOL]: {
        name: 'test-route',
        metadata: {
          annotations: {
            [K8sAnnotations.AccessAdmin]: 'true',
            [K8sAnnotations.AccessWrite]: 'true',
          },
        },
      },
    };

    const user = userEvent.setup();

    renderPolicy(
      <Policy
        isDefaultPolicy
        currentRoute={routeWithAdmin}
        alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
        onEditPolicy={noop}
        onAddPolicy={noop}
        onDeletePolicy={noop}
        onShowAlertInstances={noop}
      />
    );

    const defaultPolicy = screen.getByTestId('am-root-route-container');
    await user.click(within(defaultPolicy).getByTestId('more-actions'));

    expect(screen.getByRole('menuitem', { name: 'Manage permissions' })).toBeInTheDocument();
  });

  it('should not show manage permissions menu item when route lacks canAdmin annotation', async () => {
    const routeWithoutAdmin: RouteWithID = {
      id: '0',
      receiver: 'grafana-default-email',
      group_by: ['grafana_folder', 'alertname'],
      routes: [],
      group_wait: '30s',
      group_interval: '5m',
      repeat_interval: '4h',
    };

    const user = userEvent.setup();

    renderPolicy(
      <Policy
        isDefaultPolicy
        currentRoute={routeWithoutAdmin}
        alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
        onEditPolicy={noop}
        onAddPolicy={noop}
        onDeletePolicy={noop}
        onShowAlertInstances={noop}
      />
    );

    const defaultPolicy = screen.getByTestId('am-root-route-container');
    await user.click(within(defaultPolicy).getByTestId('more-actions'));

    expect(screen.queryByRole('menuitem', { name: 'Manage permissions' })).not.toBeInTheDocument();
  });

  describe('"Add route" button entity-level write permission', () => {
    const baseRoute: RouteWithID = {
      id: '0',
      receiver: 'grafana-default-email',
      routes: [],
    };

    it('should hide Add route button when canWrite annotation is false', () => {
      const route: RouteWithID = {
        ...baseRoute,
        [ROUTES_META_SYMBOL]: {
          name: 'test-route',
          metadata: { annotations: { [K8sAnnotations.AccessWrite]: 'false' } },
        },
      };

      renderPolicy(
        <Policy
          isDefaultPolicy
          currentRoute={route}
          alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
          onEditPolicy={noop}
          onAddPolicy={noop}
          onDeletePolicy={noop}
          onShowAlertInstances={noop}
        />
      );

      expect(screen.queryByRole('button', { name: /add route/i })).not.toBeInTheDocument();
    });

    it('should show Add route button when canWrite annotation is true', () => {
      const route: RouteWithID = {
        ...baseRoute,
        [ROUTES_META_SYMBOL]: {
          name: 'test-route',
          metadata: { annotations: { [K8sAnnotations.AccessWrite]: 'true' } },
        },
      };

      renderPolicy(
        <Policy
          isDefaultPolicy
          currentRoute={route}
          alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
          onEditPolicy={noop}
          onAddPolicy={noop}
          onDeletePolicy={noop}
          onShowAlertInstances={noop}
        />
      );

      expect(screen.getByRole('button', { name: /add route/i })).toBeInTheDocument();
    });

    it('should show Add route button when there is no K8s metadata (falls back to RBAC)', () => {
      renderPolicy(
        <Policy
          isDefaultPolicy
          currentRoute={baseRoute}
          alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
          onEditPolicy={noop}
          onAddPolicy={noop}
          onDeletePolicy={noop}
          onShowAlertInstances={noop}
        />
      );

      expect(screen.getByRole('button', { name: /add route/i })).toBeInTheDocument();
    });
  });
});

// Doesn't matter which path the routes use, it just needs to match the initialEntries history entry to render the element
const renderPolicy = (element: JSX.Element) =>
  render(
    <Routes>
      <Route path={'/'} element={<AlertmanagerProvider accessType="notification">{element}</AlertmanagerProvider>} />
    </Routes>,
    {
      historyOptions: {
        initialEntries: ['/'],
      },
    }
  );

const eq = MatcherOperator.equal;

const mockRoutes: RouteWithID = {
  id: '0',
  receiver: 'grafana-default-email',
  group_by: ['grafana_folder', 'alertname'],
  routes: [
    {
      id: '1',
      receiver: 'provisioned-contact-point',
      object_matchers: [['team', eq, 'operations']],
      mute_time_intervals: ['mt-1'],
      active_time_intervals: ['mt-2'],
      continue: true,
      routes: [
        {
          id: '2',
          object_matchers: [['region', eq, 'EMEA']],
        },
        {
          id: '3',
          receiver: 'grafana-default-email',
          object_matchers: [
            ['foo', eq, 'bar'],
            ['bar', eq, 'baz'],
            ['baz', eq, 'qux'],
            ['asdf', eq, 'asdf'],
            ['type', eq, 'disk'],
            ['severity', eq, 'critical'],
          ],
        },
      ],
    },
  ],
  group_wait: '30s',
  group_interval: '5m',
  repeat_interval: '4h',
};

describe('isAutoGeneratedRoot', () => {
  it('returns false when object_matchers is not defined', () => {
    const route: RouteWithID = {
      id: '1',
    };
    expect(isAutoGeneratedRoot(route)).toBe(false);
  });

  it('returns true when object_matchers contains AUTOGENERATED_ROOT_LABEL_NAME', () => {
    const route: RouteWithID = {
      id: '1',
      object_matchers: [[AUTOGENERATED_ROOT_LABEL_NAME, MatcherOperator.equal, 'true']],
    };
    expect(isAutoGeneratedRoot(route)).toBe(true);
  });

  it('returns false when object_matchers does not contain AUTOGENERATED_ROOT_LABEL_NAME', () => {
    const route: RouteWithID = {
      id: '1',
      object_matchers: [['label', MatcherOperator.equal, 'true']],
    };
    expect(isAutoGeneratedRoot(route)).toBe(false);
  });
});

describe('useCreateDropdownMenuActions', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });
  const openDetailModal = jest.fn();
  const currentRoute: RouteWithID = { id: '0', routes: [{ id: '1' }] };
  const toggleShowExportDrawer = jest.fn();
  const onDeletePolicy = jest.fn();
  const testCases = [
    {
      isAutoGenerated: false,
      isDefaultPolicy: true,
      provenance: undefined,
      expectedMenu: ['edit-policy', 'export-policy'],
    },
    {
      isAutoGenerated: false,
      isDefaultPolicy: true,
      provenance: KnownProvenance.File,
      expectedMenu: ['edit-policy', 'export-policy'],
    },
    {
      isAutoGenerated: false,
      isDefaultPolicy: false,
      provenance: undefined,
      expectedMenu: ['edit-policy', 'delete-policy'],
    },
    {
      isAutoGenerated: false,
      isDefaultPolicy: false,
      provenance: KnownProvenance.File,
      expectedMenu: ['edit-policy', 'delete-policy'],
    },
    { isAutoGenerated: true, isDefaultPolicy: true, provenance: KnownProvenance.File, expectedMenu: ['edit-policy'] },
    { isAutoGenerated: true, isDefaultPolicy: false, provenance: undefined, expectedMenu: ['edit-policy'] },
    { isAutoGenerated: true, isDefaultPolicy: true, provenance: undefined, expectedMenu: ['edit-policy'] },
    { isAutoGenerated: true, isDefaultPolicy: false, provenance: KnownProvenance.File, expectedMenu: ['edit-policy'] },
  ];

  testCases.forEach(({ isAutoGenerated, isDefaultPolicy, provenance, expectedMenu }) => {
    const provisionedStatus = provenance ? 'provisioned' : 'not provisioned';
    it(`Having all the permissions returns ${expectedMenu.length} menu items for isAutoGenerated=${isAutoGenerated}, isDefaultPolicy=${isDefaultPolicy}, ${provisionedStatus}`, () => {
      useAlertmanagerAbilitiesMock.mockReturnValue([
        [true, true],
        [true, true],
        [true, true],
      ]);
      const { result } = renderHook(() =>
        useCreateDropdownMenuActions(
          isAutoGenerated,
          isDefaultPolicy,
          true,
          provenance,
          openDetailModal,
          currentRoute,
          toggleShowExportDrawer,
          onDeletePolicy
        )
      );

      const menuItemsKeys = result.current.map((item) => item.key ?? '');
      expect(menuItemsKeys).toEqual(expectedMenu);
    });
  });

  describe('K8s metadata permission overrides', () => {
    const makeRouteWithK8sMetadata = (annotations: Record<string, string>): RouteWithID => ({
      id: '0',
      routes: [{ id: '1' }],
      [ROUTES_META_SYMBOL]: {
        name: 'test-route',
        metadata: { annotations },
      },
    });

    it('uses K8s entity annotations for edit/delete when metadata is present', () => {
      useAlertmanagerAbilitiesMock.mockReturnValue([
        [true, true],
        [true, true],
        [true, true],
      ]);

      const route = makeRouteWithK8sMetadata({
        [K8sAnnotations.AccessWrite]: 'true',
        [K8sAnnotations.AccessDelete]: 'true',
      });

      const { result } = renderHook(() =>
        useCreateDropdownMenuActions(
          false,
          false,
          true,
          undefined,
          openDetailModal,
          route,
          toggleShowExportDrawer,
          onDeletePolicy
        )
      );

      const keys = result.current.map((item) => item.key ?? '');
      expect(keys).toContain('edit-policy');
      expect(keys).toContain('delete-policy');
    });

    it('hides edit when K8s canWrite annotation is false, even with RBAC allowed', () => {
      useAlertmanagerAbilitiesMock.mockReturnValue([
        [true, true],
        [true, true],
        [true, true],
      ]);

      const route = makeRouteWithK8sMetadata({
        [K8sAnnotations.AccessWrite]: 'false',
        [K8sAnnotations.AccessDelete]: 'true',
      });

      const { result } = renderHook(() =>
        useCreateDropdownMenuActions(
          false,
          false,
          true,
          undefined,
          openDetailModal,
          route,
          toggleShowExportDrawer,
          onDeletePolicy
        )
      );

      const keys = result.current.map((item) => item.key ?? '');
      expect(keys).not.toContain('edit-policy');
      expect(keys).toContain('delete-policy');
    });

    it('hides delete when K8s canDelete annotation is false, even with RBAC allowed', () => {
      useAlertmanagerAbilitiesMock.mockReturnValue([
        [true, true],
        [true, true],
        [true, true],
      ]);

      const route = makeRouteWithK8sMetadata({
        [K8sAnnotations.AccessWrite]: 'true',
        [K8sAnnotations.AccessDelete]: 'false',
      });

      const { result } = renderHook(() =>
        useCreateDropdownMenuActions(
          false,
          false,
          true,
          undefined,
          openDetailModal,
          route,
          toggleShowExportDrawer,
          onDeletePolicy
        )
      );

      const keys = result.current.map((item) => item.key ?? '');
      expect(keys).toContain('edit-policy');
      expect(keys).not.toContain('delete-policy');
    });

    it('falls back to RBAC abilities when no K8s metadata is present', () => {
      useAlertmanagerAbilitiesMock.mockReturnValue([
        [true, true],
        [true, true],
        [true, true],
      ]);

      const route: RouteWithID = { id: '0', routes: [{ id: '1' }] };

      const { result } = renderHook(() =>
        useCreateDropdownMenuActions(
          false,
          false,
          true,
          undefined,
          openDetailModal,
          route,
          toggleShowExportDrawer,
          onDeletePolicy
        )
      );

      const keys = result.current.map((item) => item.key ?? '');
      expect(keys).toContain('edit-policy');
      expect(keys).toContain('delete-policy');
    });

    it('falls back to RBAC and hides edit when RBAC disallows, without K8s metadata', () => {
      useAlertmanagerAbilitiesMock.mockReturnValue([
        [true, false],
        [true, true],
        [true, true],
      ]);

      const route: RouteWithID = { id: '0', routes: [{ id: '1' }] };

      const { result } = renderHook(() =>
        useCreateDropdownMenuActions(
          false,
          false,
          true,
          undefined,
          openDetailModal,
          route,
          toggleShowExportDrawer,
          onDeletePolicy
        )
      );

      const keys = result.current.map((item) => item.key ?? '');
      expect(keys).not.toContain('edit-policy');
      expect(keys).toContain('delete-policy');
    });
  });

  describe('onManagePermissions', () => {
    it('includes manage-permissions menu item when onManagePermissions is provided', () => {
      useAlertmanagerAbilitiesMock.mockReturnValue([
        [true, true],
        [true, true],
        [true, true],
      ]);

      const { result } = renderHook(() =>
        useCreateDropdownMenuActions(
          false,
          false,
          true,
          undefined,
          openDetailModal,
          currentRoute,
          toggleShowExportDrawer,
          onDeletePolicy,
          undefined,
          jest.fn()
        )
      );

      const keys = result.current.map((item) => item.key ?? '');
      expect(keys).toContain('manage-permissions');
    });

    it('does not include manage-permissions menu item when onManagePermissions is undefined', () => {
      useAlertmanagerAbilitiesMock.mockReturnValue([
        [true, true],
        [true, true],
        [true, true],
      ]);

      const { result } = renderHook(() =>
        useCreateDropdownMenuActions(
          false,
          false,
          true,
          undefined,
          openDetailModal,
          currentRoute,
          toggleShowExportDrawer,
          onDeletePolicy,
          undefined,
          undefined
        )
      );

      const keys = result.current.map((item) => item.key ?? '');
      expect(keys).not.toContain('manage-permissions');
    });
  });
});

describe('TimingOptionsMeta', () => {
  it('should render nothing without options', () => {
    render(<TimingOptionsMeta timingOptions={{}} />);
    expect(screen.queryByText(/wait/i)).not.toBeInTheDocument();
  });

  it('should render only repeat interval', () => {
    render(<TimingOptionsMeta timingOptions={{ repeat_interval: '5h' }} />);
    expect(screen.getByText(/repeated every/i)).toBeInTheDocument();
    expect(screen.getByText('5h')).toBeInTheDocument();
  });

  it('should render all options', () => {
    render(<TimingOptionsMeta timingOptions={{ group_wait: '30s', group_interval: '5m', repeat_interval: '4h' }} />);
    expect(
      first(
        screen.getAllByText(
          (_, element) =>
            element?.textContent === 'Wait 30s to group instances · Wait 5m before sending updates · Repeated every 4h',
          { collapseWhitespace: false, trim: false, exact: true }
        )
      )
    ).toBeInTheDocument();
  });
});
