import { useMemo } from 'react';

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

import { useAlertmanager } from '../../../state/AlertmanagerContext';
import { notificationsPermissions } from '../../../utils/access-control';
import { isProvisionedResource } from '../../../utils/k8s/utils';
import { isAdmin } from '../../../utils/misc';
import { makeAbility } from '../abilityUtils';
import {
  type Ability,
  Granted,
  InsufficientPermissions,
  NotSupported,
  NotificationPolicyAction,
  Provisioned,
} from '../types';

/** Minimal shape needed to check provenance on a notification policy/route. */
type NotificationPolicyContext = { provenance?: string };

export type NotificationPolicyAbilityParam =
  | { action: NotificationPolicyAction.ViewTree }
  | { action: NotificationPolicyAction.ViewAutogenerated }
  | { action: NotificationPolicyAction.Create }
  | { action: NotificationPolicyAction.UpdateTree; context: NotificationPolicyContext }
  | { action: NotificationPolicyAction.Delete; context: NotificationPolicyContext }
  | { action: NotificationPolicyAction.Export; context: NotificationPolicyContext };

const PERMISSIONS: Record<NotificationPolicyAction, AccessControlAction[]> = {
  [NotificationPolicyAction.ViewTree]: [
    notificationsPermissions.read.grafana,
    AccessControlAction.AlertingRoutesRead,
    AccessControlAction.ActionAlertingManagedRoutesRead,
  ],
  [NotificationPolicyAction.Create]: [
    notificationsPermissions.create.grafana,
    AccessControlAction.AlertingRoutesWrite,
    AccessControlAction.ActionAlertingManagedRoutesCreate,
    AccessControlAction.ActionAlertingManagedRoutesWrite,
    AccessControlAction.ActionAlertingManagedRoutesDelete,
  ],
  [NotificationPolicyAction.UpdateTree]: [
    notificationsPermissions.update.grafana,
    AccessControlAction.AlertingRoutesWrite,
    AccessControlAction.ActionAlertingManagedRoutesCreate,
    AccessControlAction.ActionAlertingManagedRoutesWrite,
    AccessControlAction.ActionAlertingManagedRoutesDelete,
  ],
  [NotificationPolicyAction.Delete]: [
    notificationsPermissions.delete.grafana,
    AccessControlAction.AlertingRoutesWrite,
    AccessControlAction.ActionAlertingManagedRoutesCreate,
    AccessControlAction.ActionAlertingManagedRoutesWrite,
    AccessControlAction.ActionAlertingManagedRoutesDelete,
  ],
  [NotificationPolicyAction.Export]: [
    notificationsPermissions.read.grafana,
    AccessControlAction.AlertingRoutesRead,
    AccessControlAction.ActionAlertingManagedRoutesRead,
  ],
  [NotificationPolicyAction.ViewAutogenerated]: [], // gated by isAdmin(), not by RBAC permissions
};

export const PERMISSIONS_NOTIFICATION_POLICIES: AccessControlAction[] = Object.values(PERMISSIONS).flatMap(
  (permissions) => permissions
);

/**
 * Global (unscoped) notification policy ability check.
 *
 * Use this in navigation and any context outside AlertmanagerContext. Performs a pure
 * RBAC check with no alertmanager-type gate. For ViewAutogenerated the isAdmin() role
 * check is still applied; scoped provenance checks are omitted.
 */
export function useGlobalNotificationPolicyAbility(action: NotificationPolicyAction): Ability {
  return useMemo(() => {
    switch (action) {
      case NotificationPolicyAction.ViewAutogenerated:
        // TODO: replace InsufficientPermissions([]) with an INSUFFICIENT_ROLE cause once the
        // Ability discriminated union gains that variant. Empty anyOfPermissions currently
        // renders as "Requires: " with nothing after it — misleading for role-gated actions.
        return isAdmin() ? Granted : InsufficientPermissions([]);
      default:
        return makeAbility(true, PERMISSIONS[action]);
    }
  }, [action]);
}

export function useNotificationPolicyAbility(payload: NotificationPolicyAbilityParam): Ability {
  const { hasConfigurationAPI, isGrafanaAlertmanager } = useAlertmanager();
  const action = payload.action;
  const provenance = 'context' in payload ? payload.context.provenance : undefined;

  return useMemo(() => {
    const actionPermission = PERMISSIONS[action];

    switch (action) {
      // Viewing the policy tree is always supported — it is a read-only operation
      // that works for any alertmanager type (including Prometheus / read-only AMs).
      case NotificationPolicyAction.ViewTree:
        return makeAbility(true, actionPermission);

      case NotificationPolicyAction.Create:
        return makeAbility(hasConfigurationAPI, actionPermission);

      // The autogenerated policy tree is only visible to org admins — it has no RBAC permission
      // of its own. This matches the pre-refactor behaviour where admins got Granted and
      // non-admins got InsufficientPermissions([]).
      // TODO: replace InsufficientPermissions([]) with an INSUFFICIENT_ROLE cause once the
      // Ability discriminated union gains that variant.
      case NotificationPolicyAction.ViewAutogenerated:
        if (!hasConfigurationAPI) {
          return NotSupported;
        }
        return isAdmin() ? Granted : InsufficientPermissions([]);

      case NotificationPolicyAction.UpdateTree:
      case NotificationPolicyAction.Delete: {
        if (!hasConfigurationAPI) {
          return NotSupported;
        }
        if (isProvisionedResource(provenance)) {
          return Provisioned;
        }
        return makeAbility(true, actionPermission);
      }

      case NotificationPolicyAction.Export: {
        // Provisioning export (/api/v1/provisioning/policies/export) is Grafana AM only.
        // Supports multiple routing trees via the routeName query param when alertingMultiplePolicies
        // is enabled. Export is read-only — provisioned policies can be exported (same as mute timings).
        if (!hasConfigurationAPI) {
          return NotSupported;
        }
        return makeAbility(isGrafanaAlertmanager, actionPermission);
      }
    }
  }, [action, provenance, hasConfigurationAPI, isGrafanaAlertmanager]);
}
