import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:ar_autopilot_display/theme/theme_registry.dart';
import 'package:ar_autopilot_display/theme/autopilot_theme.dart';

// ── WCAG 2.1 helpers ──────────────────────────────────────────────────────────

/// WCAG 2.1 relative luminance of a colour.
/// https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
double _luminance(Color c) {
  double lin(int channel) {
    final s = channel / 255.0;
    return s <= 0.04045 ? s / 12.92 : pow((s + 0.055) / 1.055, 2.4).toDouble();
  }

  return 0.2126 * lin(c.red) + 0.7152 * lin(c.green) + 0.0722 * lin(c.blue);
}

/// WCAG 2.1 contrast ratio (symmetric).
double _contrast(Color a, Color b) {
  final la = _luminance(a);
  final lb = _luminance(b);
  final lighter = max(la, lb);
  final darker = min(la, lb);
  return (lighter + 0.05) / (darker + 0.05);
}

/// Returns all colour stops from a [Gradient].
List<Color> _gradientColors(Gradient g) {
  if (g is LinearGradient) return g.colors;
  if (g is RadialGradient) return g.colors;
  return [];
}

/// Returns the **maximum** contrast of [text] against any stop in [bg].
///
/// We test the best-case stop: the gradient stop that gives the highest
/// contrast against the text colour. This ensures the design intent is
/// achievable — the text will visually overlap the highest-contrast region.
///
/// For DISENGAGE (≥ 7:1 AAA): tests that at least one region of the gradient
/// button delivers the required accessibility threshold.
double _maxContrastVsGradient(Color text, Gradient bg) {
  final stops = _gradientColors(bg);
  if (stops.isEmpty) return 0;
  return stops.map((c) => _contrast(text, c)).reduce(max);
}

// ── Tests ─────────────────────────────────────────────────────────────────────

void main() {
  group('WCAG contrast — mandatory Sprint 4 gates', () {
    for (final theme in ThemeRegistry.all) {
      _contrastTests(theme);
    }
  });
}

void _contrastTests(AutopilotTheme t) {
  group('Theme: ${t.id}', () {
    // ── DISENGAGE ─────────────────────────────────────────────────────────────
    test('DISENGAGE text vs button background ≥ 7:1 (WCAG AAA)', () {
      final ratio = _maxContrastVsGradient(t.disengageText, t.disengageBackground);
      expect(
        ratio,
        greaterThanOrEqualTo(7.0),
        reason:
            '${t.id}: DISENGAGE text contrast is ${ratio.toStringAsFixed(2)}:1 — '
            'must be ≥ 7:1 (AAA). Adjust disengageText or disengageBackground.',
      );
    });

    // ── Action buttons ────────────────────────────────────────────────────────
    test('Action button text vs button background ≥ 4.5:1 (WCAG AA)', () {
      final ratio = _maxContrastVsGradient(
        t.actionButtonText,
        t.actionButtonBackground,
      );
      expect(
        ratio,
        greaterThanOrEqualTo(4.5),
        reason:
            '${t.id}: action button text contrast is ${ratio.toStringAsFixed(2)}:1 — '
            'must be ≥ 4.5:1 (AA). Operators must read DODGE/±1°/±10° without hover.',
      );
    });

    // ── Body text ─────────────────────────────────────────────────────────────
    test('textMain vs background ≥ 4.5:1 (WCAG AA)', () {
      final ratio = _contrast(t.textMain, t.background);
      expect(
        ratio,
        greaterThanOrEqualTo(4.5),
        reason:
            '${t.id}: textMain contrast is ${ratio.toStringAsFixed(2)}:1 — '
            'must be ≥ 4.5:1 (AA).',
      );
    });

    // ── Set-point legibility ──────────────────────────────────────────────────
    test('setLight vs background ≥ 3:1 (large UI element)', () {
      final ratio = _contrast(t.setLight, t.background);
      expect(
        ratio,
        greaterThanOrEqualTo(3.0),
        reason:
            '${t.id}: setLight contrast is ${ratio.toStringAsFixed(2)}:1 — '
            'must be ≥ 3:1 (set-point is a large graphical element).',
      );
    });

    // ── OK / Warn semantic colours ────────────────────────────────────────────
    test('okColor vs background ≥ 3:1 (status indicator)', () {
      final ratio = _contrast(t.okColor, t.background);
      expect(
        ratio,
        greaterThanOrEqualTo(3.0),
        reason:
            '${t.id}: okColor contrast is ${ratio.toStringAsFixed(2)}:1 — '
            'status indicators require ≥ 3:1.',
      );
    });
  });
}
