// Per-source mapping from raw event action strings to the dotted verbs we
// store on AuditEvent.action. Known actions get a clean explicit verb; unknown
// ones fall through to `<source>.<raw>` so we don't silently drop new event
// types Authentik / Stalwart / OCIS introduces.

import type { AuditOutcome, AuditResourceType } from '../schemas/audit-event.schema.js'

export interface MappedAction {
  action: string
  outcome: AuditOutcome
  resourceType?: AuditResourceType
}

// Authentik action strings (from /api/v3/events/events/ — see
// https://goauthentik.io/docs/events/ for the full enum).
const AUTHENTIK_MAP: Record<string, MappedAction> = {
  login:                    { action: 'authentik.login',                   outcome: 'success', resourceType: 'user' },
  login_failed:             { action: 'authentik.login_failed',            outcome: 'failure', resourceType: 'user' },
  logout:                   { action: 'authentik.logout',                  outcome: 'success', resourceType: 'user' },
  user_write:               { action: 'authentik.user_updated',            outcome: 'success', resourceType: 'user' },
  password_set:             { action: 'authentik.password_changed',        outcome: 'success', resourceType: 'user' },
  secret_rotate:            { action: 'authentik.secret_rotated',          outcome: 'success', resourceType: 'system' },
  group_membership_set:     { action: 'authentik.group_membership_changed', outcome: 'success', resourceType: 'user' },
  invitation_used:          { action: 'authentik.invitation_used',         outcome: 'success', resourceType: 'user' },
  authorize_application:    { action: 'authentik.app_authorized',          outcome: 'success', resourceType: 'user' },
  impersonation_started:    { action: 'authentik.impersonation_started',   outcome: 'success', resourceType: 'user' },
  impersonation_ended:      { action: 'authentik.impersonation_ended',     outcome: 'success', resourceType: 'user' },
  policy_execution:         { action: 'authentik.policy_executed',         outcome: 'success', resourceType: 'system' },
  policy_exception:         { action: 'authentik.policy_exception',        outcome: 'failure', resourceType: 'system' },
  configuration_error:      { action: 'authentik.configuration_error',     outcome: 'failure', resourceType: 'system' },
  model_created:            { action: 'authentik.model_created',           outcome: 'success', resourceType: 'system' },
  model_updated:            { action: 'authentik.model_updated',           outcome: 'success', resourceType: 'system' },
  model_deleted:            { action: 'authentik.model_deleted',           outcome: 'success', resourceType: 'system' },
}

export function mapAuthentikAction(raw: string): MappedAction {
  return (
    AUTHENTIK_MAP[raw] ?? {
      action: `authentik.${raw}`,
      outcome: 'success',
    }
  )
}

// Stalwart event categories (from the [webhook] subscription list). Stalwart
// uses dot-notation event types (auth.failure, account.created, …). Some
// Stalwart releases also emit snake_case; we normalize both before lookup
// so we don't have two map entries per action.
const STALWART_MAP: Record<string, MappedAction> = {
  'auth.success':              { action: 'stalwart.auth_success',         outcome: 'success', resourceType: 'user' },
  'auth.failure':              { action: 'stalwart.auth_failed',          outcome: 'failure', resourceType: 'user' },
  'auth.banned':               { action: 'stalwart.auth_banned',          outcome: 'failure', resourceType: 'user' },
  'account.created':           { action: 'stalwart.account_created',      outcome: 'success', resourceType: 'user' },
  'account.deleted':           { action: 'stalwart.account_deleted',      outcome: 'success', resourceType: 'user' },
  'account.password-changed':  { action: 'stalwart.password_changed',     outcome: 'success', resourceType: 'user' },
  'message.received':          { action: 'stalwart.mail_received',        outcome: 'success', resourceType: 'system' },
  'message.delivered':         { action: 'stalwart.mail_delivered',       outcome: 'success', resourceType: 'system' },
  'message.failed':            { action: 'stalwart.mail_failed',          outcome: 'failure', resourceType: 'system' },
  'message.rejected':          { action: 'stalwart.mail_rejected',        outcome: 'failure', resourceType: 'system' },
  'policy.rejection':          { action: 'stalwart.policy_rejection',     outcome: 'failure', resourceType: 'system' },
  'dkim.failure':              { action: 'stalwart.dkim_failure',         outcome: 'failure', resourceType: 'system' },
  'dmarc.failure':             { action: 'stalwart.dmarc_failure',        outcome: 'failure', resourceType: 'system' },
  'spam.detected':             { action: 'stalwart.spam_detected',        outcome: 'failure', resourceType: 'system' },
}

export function mapStalwartAction(raw: string): MappedAction {
  // Stalwart's event types sometimes show up as 'auth-failure' (kebab) or
  // 'auth_failure' (snake) depending on version; collapse to the canonical
  // dotted form before lookup.
  const normalized = raw.replace(/[-_]/g, (m) => (m === '-' ? '.' : '.'))
  return (
    STALWART_MAP[raw] ??
    STALWART_MAP[normalized] ?? {
      action: `stalwart.${raw.replace(/[.\-]/g, '_')}`,
      outcome: 'success',
    }
  )
}

// OCIS audit events. The full event vocabulary is large (~50+ types); we only
// MAP the mutations we care about and the OCIS_AUDIT_ACTIONS allowlist in the
// ocis ingest worker filters everything else out before we even look it up.
// Each OCIS event type is PascalCase: UserCreated, FileUploaded, ShareCreated.
const OCIS_MAP: Record<string, MappedAction> = {
  UserCreated:          { action: 'ocis.user_created',      outcome: 'success', resourceType: 'user' },
  UserDeleted:          { action: 'ocis.user_deleted',      outcome: 'success', resourceType: 'user' },
  UserFeatureChanged:   { action: 'ocis.user_updated',      outcome: 'success', resourceType: 'user' },
  GroupCreated:         { action: 'ocis.group_created',     outcome: 'success', resourceType: 'system' },
  GroupDeleted:         { action: 'ocis.group_deleted',     outcome: 'success', resourceType: 'system' },
  GroupMemberAdded:     { action: 'ocis.group_member_added',   outcome: 'success', resourceType: 'user' },
  GroupMemberRemoved:   { action: 'ocis.group_member_removed', outcome: 'success', resourceType: 'user' },
  SpaceCreated:         { action: 'ocis.space_created',     outcome: 'success', resourceType: 'tenant' },
  SpaceUpdated:         { action: 'ocis.space_updated',     outcome: 'success', resourceType: 'tenant' },
  SpaceDisabled:        { action: 'ocis.space_disabled',    outcome: 'success', resourceType: 'tenant' },
  SpaceDeleted:         { action: 'ocis.space_deleted',     outcome: 'success', resourceType: 'tenant' },
  SpaceShared:          { action: 'ocis.space_shared',      outcome: 'success', resourceType: 'tenant' },
  SpaceUnshared:        { action: 'ocis.space_unshared',    outcome: 'success', resourceType: 'tenant' },
  ShareCreated:         { action: 'ocis.share_created',     outcome: 'success', resourceType: 'system' },
  ShareUpdated:         { action: 'ocis.share_updated',     outcome: 'success', resourceType: 'system' },
  ShareRemoved:         { action: 'ocis.share_removed',     outcome: 'success', resourceType: 'system' },
  LinkCreated:          { action: 'ocis.link_created',      outcome: 'success', resourceType: 'system' },
  LinkUpdated:          { action: 'ocis.link_updated',      outcome: 'success', resourceType: 'system' },
  LinkRemoved:          { action: 'ocis.link_removed',      outcome: 'success', resourceType: 'system' },
  FileUploaded:         { action: 'ocis.file_uploaded',     outcome: 'success', resourceType: 'system' },
  FileTrashed:          { action: 'ocis.file_trashed',      outcome: 'success', resourceType: 'system' },
  FileRestored:         { action: 'ocis.file_restored',     outcome: 'success', resourceType: 'system' },
  FilePurged:           { action: 'ocis.file_purged',       outcome: 'success', resourceType: 'system' },
  FileVersionRestored:  { action: 'ocis.file_version_restored', outcome: 'success', resourceType: 'system' },
}

// Allowlist of OCIS event types we record. Everything else (FileDownloaded,
// UserSignedIn, etc.) is filtered before mapping — keeps the audit timeline
// scannable instead of drowning in file-read events.
export const OCIS_ALLOWLIST = new Set(Object.keys(OCIS_MAP))

export function mapOcisAction(raw: string): MappedAction | null {
  return OCIS_MAP[raw] ?? null
}
