import type { LicenseApplication } from '@ema-platform/api';
import { dateDisplayer } from '@ema-platform/shared';

/** Amber once this much of the window has been consumed. */
const WARNING_RATIO = 0.7;

const HOUR_MS = 60 * 60 * 1000;

export interface SlaState {
  state: 'ok' | 'warning' | 'breached' | 'untracked' | 'decided';
  /** Mantine colour. Always paired with `label` — never colour alone. */
  color: string;
  /** Short text for the badge, e.g. "2d left" or "Overdue 6h". */
  label: string;
  /** The full explanation, including the target, for the tooltip. */
  tooltip: string;
  /** Fraction of the window used, clamped to 0..1. */
  ratio: number;
}

/** Loose enough to accept i18next's real `t` structurally; callers pass that
 *  one, and a plain function falls back to English by resolving
 *  `defaultValue` itself, so `computeSla` still works with no i18n context
 *  (the CSV export). */
export type SlaTranslate = (key: string, options?: unknown) => string;

function tr(t: SlaTranslate | undefined, key: string, options: Record<string, unknown> | string): string {
  if (t) return t(key, options);
  if (typeof options === 'string') return options;
  const template = String(options['defaultValue'] ?? '');
  return template.replace(/\{\{(\w+)\}\}/g, (_, name) => String(options[name] ?? ''));
}

function formatDuration(ms: number): string {
  const hours = Math.floor(Math.abs(ms) / HOUR_MS);
  if (hours < 1) return '<1h';
  if (hours < 48) return `${hours}h`;
  return `${Math.floor(hours / 24)}d`;
}

/**
 * How an application is tracking against its licence type's SLA.
 *
 * Types with no `slaHours` are untracked rather than instantly overdue — the
 * authority has not set a target for them, which is not the same as missing
 * one. Decided applications stop the clock: an approval that took three weeks
 * is history, not an outstanding breach.
 */
export function computeSla(
  application: LicenseApplication,
  now: number = Date.now(),
  language = 'en',
  t?: SlaTranslate,
): SlaState {
  const slaHours = application.licenseType?.slaHours;
  const submittedAt = application.submittedAt;

  if (!slaHours || !submittedAt) {
    return {
      state: 'untracked',
      color: 'gray',
      label: '—',
      tooltip: tr(t, 'review.sla.untracked', 'No turnaround target is set for this licence type.'),
      ratio: 0,
    };
  }

  const submitted = new Date(submittedAt).getTime();
  const target = submitted + slaHours * HOUR_MS;
  const elapsed = (application.decidedAt ? new Date(application.decidedAt).getTime() : now) - submitted;
  const window = slaHours * HOUR_MS;
  const ratio = Math.min(Math.max(elapsed / window, 0), 1);
  const targetText = tr(t, 'review.sla.target', {
    hours: slaHours,
    date: dateDisplayer(target, language),
    defaultValue: 'Target {{hours}}h from submission ({{date}})',
  });

  if (application.decidedAt) {
    const met = elapsed <= window;
    return {
      state: 'decided',
      color: met ? 'teal' : 'gray',
      label: tr(t, met ? 'review.sla.met' : 'review.sla.missed', met ? 'Met' : 'Missed'),
      tooltip: tr(t, 'review.sla.decidedIn', {
        duration: formatDuration(elapsed),
        target: targetText,
        defaultValue: 'Decided in {{duration}}. {{target}}',
      }),
      ratio,
    };
  }

  const remaining = target - now;
  if (remaining < 0) {
    return {
      state: 'breached',
      color: 'red',
      label: tr(t, 'review.sla.overdue', {
        duration: formatDuration(remaining),
        defaultValue: 'Overdue {{duration}}',
      }),
      tooltip: tr(t, 'review.sla.overdueBy', {
        duration: formatDuration(remaining),
        target: targetText,
        defaultValue: 'Overdue by {{duration}}. {{target}}',
      }),
      ratio: 1,
    };
  }

  const used = elapsed / window;
  return {
    state: used >= WARNING_RATIO ? 'warning' : 'ok',
    color: used >= WARNING_RATIO ? 'yellow' : 'teal',
    label: tr(t, 'review.sla.left', {
      duration: formatDuration(remaining),
      defaultValue: '{{duration}} left',
    }),
    tooltip: tr(t, 'review.sla.remaining', {
      duration: formatDuration(remaining),
      target: targetText,
      defaultValue: '{{duration}} remaining. {{target}}',
    }),
    ratio,
  };
}
