import { renderHook } from 'test/test-utils';

import { AccessControlAction } from 'app/types/accessControl';

import { setupMswServer } from '../../../mockApi';
import { grantUserPermissions, grantUserRole } from '../../../mocks';
import { KnownProvenance } from '../../../types/knownProvenance';
import { isNotSupported, isProvisioned } from '../abilityUtils';
import { NotificationPolicyAction, isInsufficientPermissions } from '../types';

import {
  EXTERNAL_AM_VISIBILITY_PERMISSION,
  GRAFANA_AM_VISIBILITY_PERMISSION,
  createAlertmanagerWrapper,
  setupGrafanaAlertmanager,
  setupMimirAlertmanager,
  setupVanillaPrometheusAlertmanager,
} from './abilityTestUtils';
import { useGlobalNotificationPolicyAbility, useNotificationPolicyAbility } from './useNotificationPolicyAbility';

setupMswServer();

const notProvisionedContext = { provenance: KnownProvenance.None };
const provisionedContext = { provenance: KnownProvenance.API };

describe('useGlobalNotificationPolicyAbility', () => {
  it('grants ViewTree when notification read permission is held — no alertmanager context needed', () => {
    grantUserPermissions([AccessControlAction.AlertingRoutesRead]);

    const { result } = renderHook(() => useGlobalNotificationPolicyAbility(NotificationPolicyAction.ViewTree));

    expect(result.current.granted).toBe(true);
  });

  it('denies ViewTree when no read permission is held', () => {
    grantUserPermissions([]);
    grantUserRole('Viewer');

    const { result } = renderHook(() => useGlobalNotificationPolicyAbility(NotificationPolicyAction.ViewTree));

    expect(result.current.granted).toBe(false);
  });

  it('grants ViewAutogenerated to org admins', () => {
    grantUserPermissions([]);
    grantUserRole('Admin');

    const { result } = renderHook(() => useGlobalNotificationPolicyAbility(NotificationPolicyAction.ViewAutogenerated));

    expect(result.current.granted).toBe(true);
  });

  it('denies ViewAutogenerated to non-admins', () => {
    grantUserPermissions([]);
    grantUserRole('Viewer');

    const { result } = renderHook(() => useGlobalNotificationPolicyAbility(NotificationPolicyAction.ViewAutogenerated));

    expect(result.current.granted).toBe(false);
    expect(isInsufficientPermissions(result.current)).toBe(true);
  });

  it('grants Create when write permission is held', () => {
    grantUserPermissions([AccessControlAction.AlertingRoutesWrite]);

    const { result } = renderHook(() => useGlobalNotificationPolicyAbility(NotificationPolicyAction.Create));

    expect(result.current.granted).toBe(true);
  });
});

describe('useNotificationPolicyAbility', () => {
  describe('read-only actions', () => {
    it('should grant ViewTree when notification read permission is held', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([GRAFANA_AM_VISIBILITY_PERMISSION, AccessControlAction.AlertingRoutesRead]);

      const { result } = renderHook(() => useNotificationPolicyAbility({ action: NotificationPolicyAction.ViewTree }), {
        wrapper: createAlertmanagerWrapper(amSource),
      });

      expect(result.current.granted).toBe(true);
    });

    it('should grant ViewAutogenerated to org admins', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([GRAFANA_AM_VISIBILITY_PERMISSION]);
      grantUserRole('Admin');

      const { result } = renderHook(
        () => useNotificationPolicyAbility({ action: NotificationPolicyAction.ViewAutogenerated }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      expect(result.current.granted).toBe(true);
    });

    it('should deny ViewAutogenerated to non-admins', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([GRAFANA_AM_VISIBILITY_PERMISSION]);
      grantUserRole('Viewer');

      const { result } = renderHook(
        () => useNotificationPolicyAbility({ action: NotificationPolicyAction.ViewAutogenerated }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      expect(result.current.granted).toBe(false);
      expect(isInsufficientPermissions(result.current)).toBe(true);
    });

    it('should deny ViewTree when no read permission is held', () => {
      // Note: GRAFANA_AM_VISIBILITY_PERMISSION (AlertingNotificationsRead) is itself one of
      // the permissions that gates ViewTree, so we must grant no permissions here to test denial.
      // The AM will not resolve in context (no visibility permission), but ViewTree is a pure
      // RBAC check with no context dependency, so the result is still correct.
      // Also reset any role spy from previous tests so isAdmin() returns false.
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([]);
      grantUserRole('Viewer');

      const { result } = renderHook(() => useNotificationPolicyAbility({ action: NotificationPolicyAction.ViewTree }), {
        wrapper: createAlertmanagerWrapper(amSource),
      });

      expect(result.current.granted).toBe(false);
    });
  });

  describe('Create', () => {
    it('should grant Create when write permission is held', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([GRAFANA_AM_VISIBILITY_PERMISSION, AccessControlAction.AlertingRoutesWrite]);

      const { result } = renderHook(() => useNotificationPolicyAbility({ action: NotificationPolicyAction.Create }), {
        wrapper: createAlertmanagerWrapper(amSource),
      });

      expect(result.current.granted).toBe(true);
    });

    it('should deny Create when only read permission is held', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([GRAFANA_AM_VISIBILITY_PERMISSION, AccessControlAction.AlertingRoutesRead]);

      const { result } = renderHook(() => useNotificationPolicyAbility({ action: NotificationPolicyAction.Create }), {
        wrapper: createAlertmanagerWrapper(amSource),
      });

      expect(result.current.granted).toBe(false);
    });
  });

  describe('UpdateTree', () => {
    it('should grant UpdateTree when write permission is held and policy is not provisioned', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([GRAFANA_AM_VISIBILITY_PERMISSION, AccessControlAction.AlertingRoutesWrite]);

      const { result } = renderHook(
        () =>
          useNotificationPolicyAbility({ action: NotificationPolicyAction.UpdateTree, context: notProvisionedContext }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      expect(result.current.granted).toBe(true);
    });

    it('should return Provisioned for UpdateTree when policy is provisioned', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([GRAFANA_AM_VISIBILITY_PERMISSION, AccessControlAction.AlertingRoutesWrite]);

      const { result } = renderHook(
        () =>
          useNotificationPolicyAbility({ action: NotificationPolicyAction.UpdateTree, context: provisionedContext }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      expect(isProvisioned(result.current)).toBe(true);
    });
  });

  describe('Delete', () => {
    it('should grant Delete when write permission is held and policy is not provisioned', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([GRAFANA_AM_VISIBILITY_PERMISSION, AccessControlAction.AlertingRoutesWrite]);

      const { result } = renderHook(
        () => useNotificationPolicyAbility({ action: NotificationPolicyAction.Delete, context: notProvisionedContext }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      expect(result.current.granted).toBe(true);
    });

    it('should return Provisioned for Delete when policy is provisioned', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([GRAFANA_AM_VISIBILITY_PERMISSION, AccessControlAction.AlertingRoutesWrite]);

      const { result } = renderHook(
        () => useNotificationPolicyAbility({ action: NotificationPolicyAction.Delete, context: provisionedContext }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      expect(isProvisioned(result.current)).toBe(true);
    });
  });

  describe('Export', () => {
    it('should grant Export when AlertingNotificationsRead is held and policy is not provisioned', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([GRAFANA_AM_VISIBILITY_PERMISSION]);

      const { result } = renderHook(
        () => useNotificationPolicyAbility({ action: NotificationPolicyAction.Export, context: notProvisionedContext }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      expect(result.current.granted).toBe(true);
    });

    it('should grant Export when only AlertingRoutesRead is held (legacy read permission)', () => {
      const amSource = setupGrafanaAlertmanager();
      // Grant only the legacy routes:read permission, not AlertingNotificationsRead.
      // The backend accepts this permission for export — the frontend must too.
      grantUserPermissions([AccessControlAction.AlertingRoutesRead]);

      const { result } = renderHook(
        () => useNotificationPolicyAbility({ action: NotificationPolicyAction.Export, context: notProvisionedContext }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      expect(result.current.granted).toBe(true);
    });

    it('should grant Export when only ActionAlertingManagedRoutesRead is held (k8s read permission)', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([AccessControlAction.ActionAlertingManagedRoutesRead]);

      const { result } = renderHook(
        () => useNotificationPolicyAbility({ action: NotificationPolicyAction.Export, context: notProvisionedContext }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      expect(result.current.granted).toBe(true);
    });

    it('should grant Export when policy is provisioned — export is read-only', () => {
      const amSource = setupGrafanaAlertmanager();
      grantUserPermissions([GRAFANA_AM_VISIBILITY_PERMISSION]);

      const { result } = renderHook(
        () => useNotificationPolicyAbility({ action: NotificationPolicyAction.Export, context: provisionedContext }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      expect(result.current.granted).toBe(true);
    });

    it('should return NotSupported for Export on external Mimir alertmanager — provisioning export is Grafana AM only', () => {
      const amSource = setupMimirAlertmanager();
      grantUserPermissions([EXTERNAL_AM_VISIBILITY_PERMISSION, AccessControlAction.AlertingNotificationsExternalWrite]);

      const { result } = renderHook(
        () => useNotificationPolicyAbility({ action: NotificationPolicyAction.Export, context: notProvisionedContext }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      // Export uses /api/v1/provisioning/policies/export, which is scoped to the built-in Grafana
      // Alertmanager. Cloud/external AMs must not expose this action until export reads from the
      // selected alertmanager config instead.
      expect(isNotSupported(result.current)).toBe(true);
    });
  });

  describe('vanilla Prometheus alertmanager', () => {
    it('should return NotSupported for mutating actions — no configuration API available', () => {
      const amSource = setupVanillaPrometheusAlertmanager();
      grantUserPermissions([]);

      const { result } = renderHook(
        () => ({
          viewTree: useNotificationPolicyAbility({ action: NotificationPolicyAction.ViewTree }),
          viewAutogenerated: useNotificationPolicyAbility({ action: NotificationPolicyAction.ViewAutogenerated }),
          create: useNotificationPolicyAbility({ action: NotificationPolicyAction.Create }),
          updateTree: useNotificationPolicyAbility({
            action: NotificationPolicyAction.UpdateTree,
            context: notProvisionedContext,
          }),
          delete: useNotificationPolicyAbility({
            action: NotificationPolicyAction.Delete,
            context: notProvisionedContext,
          }),
          export: useNotificationPolicyAbility({
            action: NotificationPolicyAction.Export,
            context: notProvisionedContext,
          }),
        }),
        { wrapper: createAlertmanagerWrapper(amSource) }
      );

      // ViewTree is always supported (read-only); without permissions it is InsufficientPermissions.
      expect(isInsufficientPermissions(result.current.viewTree)).toBe(true);
      expect(isNotSupported(result.current.viewAutogenerated)).toBe(true);
      expect(isNotSupported(result.current.create)).toBe(true);
      expect(isNotSupported(result.current.updateTree)).toBe(true);
      expect(isNotSupported(result.current.delete)).toBe(true);
      expect(isNotSupported(result.current.export)).toBe(true);
    });
  });
});
