import type { ConditionKind } from '../../pages/Alerts/enums';

export type VariableType =
  | 'string'
  | 'Instant'
  | 'number'
  | 'boolean'
  | 'url'
  | 'uuid';

export interface AlertVariable {
  path: string;             // e.g. "alert.firedAt"
  type: VariableType;
  description: string;
  sampleValue: string;      // rendered as a faint suggestion preview
  availableForKinds: 'always' | ConditionKind[];
  mayBeNull?: boolean;      // show "may be null" badge in UI
}

/** Variables the spec §8 context map exposes. Add to this registry whenever
 *  NotificationContextBuilder (backend) gains a new leaf. */
export const ALERT_VARIABLES: AlertVariable[] = [
  // Always available
  { path: 'env.slug', type: 'string', description: 'Environment slug', sampleValue: 'prod', availableForKinds: 'always' },
  { path: 'env.id', type: 'uuid', description: 'Environment UUID', sampleValue: '00000000-0000-0000-0000-000000000001', availableForKinds: 'always' },
  { path: 'rule.id', type: 'uuid', description: 'Rule UUID', sampleValue: '11111111-...', availableForKinds: 'always' },
  { path: 'rule.name', type: 'string', description: 'Rule display name', sampleValue: 'Order API error rate', availableForKinds: 'always' },
  { path: 'rule.severity', type: 'string', description: 'Rule severity', sampleValue: 'CRITICAL', availableForKinds: 'always' },
  { path: 'rule.description', type: 'string', description: 'Rule description', sampleValue: 'Paging ops if error rate >5%', availableForKinds: 'always' },
  { path: 'alert.id', type: 'uuid', description: 'Alert instance UUID', sampleValue: '22222222-...', availableForKinds: 'always' },
  { path: 'alert.state', type: 'string', description: 'Alert state', sampleValue: 'FIRING', availableForKinds: 'always' },
  { path: 'alert.firedAt', type: 'Instant', description: 'When the alert fired', sampleValue: '2026-04-20T14:33:10Z', availableForKinds: 'always' },
  { path: 'alert.resolvedAt', type: 'Instant', description: 'When the alert resolved', sampleValue: '2026-04-20T14:45:00Z', availableForKinds: 'always', mayBeNull: true },
  { path: 'alert.ackedBy', type: 'string', description: 'User who ack\'d the alert', sampleValue: 'alice', availableForKinds: 'always', mayBeNull: true },
  { path: 'alert.link', type: 'url', description: 'UI link to this alert', sampleValue: 'https://cameleer.example.com/alerts/inbox/2222...', availableForKinds: 'always' },
  { path: 'alert.currentValue', type: 'number', description: 'Observed metric value', sampleValue: '0.12', availableForKinds: 'always', mayBeNull: true },
  { path: 'alert.threshold', type: 'number', description: 'Rule threshold', sampleValue: '0.05', availableForKinds: 'always', mayBeNull: true },

  // App subtree — populated on every kind except env-wide rules
  { path: 'app.slug', type: 'string', description: 'App slug', sampleValue: 'orders',
    availableForKinds: ['ROUTE_METRIC', 'EXCHANGE_MATCH', 'AGENT_STATE', 'DEPLOYMENT_STATE', 'LOG_PATTERN', 'JVM_METRIC'], mayBeNull: true },
  { path: 'app.id',   type: 'uuid',   description: 'App UUID', sampleValue: '33333333-...',
    availableForKinds: ['ROUTE_METRIC', 'EXCHANGE_MATCH', 'AGENT_STATE', 'DEPLOYMENT_STATE', 'LOG_PATTERN', 'JVM_METRIC'], mayBeNull: true },

  // AGENT_LIFECYCLE — agent + event subtree (distinct from AGENT_STATE's agent.* leaves)
  { path: 'agent.app', type: 'string', description: 'Agent app slug', sampleValue: 'orders',
    availableForKinds: ['AGENT_LIFECYCLE'] },
  { path: 'event.type',      type: 'string',  description: 'Lifecycle event type',    sampleValue: 'WENT_DEAD',
    availableForKinds: ['AGENT_LIFECYCLE'] },
  { path: 'event.timestamp', type: 'Instant', description: 'When the event happened', sampleValue: '2026-04-20T14:33:10Z',
    availableForKinds: ['AGENT_LIFECYCLE'] },
  { path: 'event.detail',    type: 'string',  description: 'Free-text event detail',  sampleValue: 'orders-0 STALE -> DEAD',
    availableForKinds: ['AGENT_LIFECYCLE'], mayBeNull: true },

  // ROUTE_METRIC + EXCHANGE_MATCH share route.*
  { path: 'route.id',  type: 'string', description: 'Route ID', sampleValue: 'route-1',
    availableForKinds: ['ROUTE_METRIC', 'EXCHANGE_MATCH'] },
  { path: 'route.uri', type: 'string', description: 'Route URI', sampleValue: 'direct:orders',
    availableForKinds: ['ROUTE_METRIC', 'EXCHANGE_MATCH'] },

  // EXCHANGE_MATCH
  { path: 'exchange.id',     type: 'string', description: 'Exchange ID',     sampleValue: 'exch-ab12',
    availableForKinds: ['EXCHANGE_MATCH'] },
  { path: 'exchange.status', type: 'string', description: 'Exchange status', sampleValue: 'FAILED',
    availableForKinds: ['EXCHANGE_MATCH'] },

  // AGENT_STATE + JVM_METRIC share agent.id/name; AGENT_STATE adds agent.state
  { path: 'agent.id',    type: 'string', description: 'Agent instance ID', sampleValue: 'prod-orders-0',
    availableForKinds: ['AGENT_STATE', 'AGENT_LIFECYCLE', 'JVM_METRIC'] },
  { path: 'agent.name',  type: 'string', description: 'Agent display name', sampleValue: 'orders-0',
    availableForKinds: ['AGENT_STATE', 'JVM_METRIC'] },
  { path: 'agent.state', type: 'string', description: 'Agent state',        sampleValue: 'DEAD',
    availableForKinds: ['AGENT_STATE'] },

  // DEPLOYMENT_STATE
  { path: 'deployment.id',     type: 'uuid',   description: 'Deployment UUID',   sampleValue: '44444444-...',
    availableForKinds: ['DEPLOYMENT_STATE'] },
  { path: 'deployment.status', type: 'string', description: 'Deployment status', sampleValue: 'FAILED',
    availableForKinds: ['DEPLOYMENT_STATE'] },

  // LOG_PATTERN — leaf names match NotificationContextBuilder (log.pattern + log.matchCount)
  { path: 'log.pattern',    type: 'string', description: 'Matched log pattern',   sampleValue: 'TimeoutException',
    availableForKinds: ['LOG_PATTERN'] },
  { path: 'log.matchCount', type: 'number', description: 'Matches in window',     sampleValue: '7',
    availableForKinds: ['LOG_PATTERN'] },

  // JVM_METRIC
  { path: 'metric.name',  type: 'string', description: 'Metric name',  sampleValue: 'heap_used_percent',
    availableForKinds: ['JVM_METRIC'] },
  { path: 'metric.value', type: 'number', description: 'Metric value', sampleValue: '92.1',
    availableForKinds: ['JVM_METRIC'] },
];

/** Filter variables to those available for the given condition kind.
 *  If kind is undefined (e.g. connection URL editor), returns only "always" vars + app.*. */
export function availableVariables(
  kind: ConditionKind | undefined,
  opts: { reducedContext?: boolean } = {},
): AlertVariable[] {
  if (opts.reducedContext) {
    return ALERT_VARIABLES.filter((v) => v.path.startsWith('env.'));
  }
  if (!kind) {
    return ALERT_VARIABLES.filter(
      (v) => v.availableForKinds === 'always',
    );
  }
  return ALERT_VARIABLES.filter(
    (v) => v.availableForKinds === 'always' || v.availableForKinds.includes(kind),
  );
}

/** Parse a Mustache template and return the set of `{{path}}` references it contains.
 *  Ignores `{{#section}}` / `{{/section}}` / `{{!comment}}` — plain variable refs only. */
export function extractReferences(template: string): string[] {
  const out: string[] = [];
  const re = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_.]*)\s*\}\}/g;
  let m;
  while ((m = re.exec(template)) !== null) out.push(m[1]);
  return out;
}

/** Find references in a template that are not in the allowed-variable set. */
export function unknownReferences(
  template: string,
  allowed: readonly AlertVariable[],
): string[] {
  const allowedSet = new Set(allowed.map((v) => v.path));
  return extractReferences(template).filter((r) => !allowedSet.has(r));
}
