import 'dart:math' as math;

import 'package:bolao_mobile/core/tema/marca.dart';
import 'package:bolao_mobile/core/tema/tema.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

/// Contraste WCAG entre duas cores opacas.
double _contraste(Color a, Color b) {
  double canal(double c) =>
      c <= 0.03928 ? c / 12.92 : math.pow((c + 0.055) / 1.055, 2.4).toDouble();
  double luz(Color c) =>
      0.2126 * canal(c.r) + 0.7152 * canal(c.g) + 0.0722 * canal(c.b);
  final la = luz(a);
  final lb = luz(b);
  return (math.max(la, lb) + 0.05) / (math.min(la, lb) + 0.05);
}

void main() {
  final cores = temaDoApp().colorScheme;

  group('Identidade', () {
    test('a cor da marca aparece no esquema, não uma aproximação', () {
      // Com o `tonalSpot` padrão do Flutter, a semente #615FF6 virava #595992 —
      // um violeta acinzentado que não é a cor da Raro. `fidelity` preserva.
      expect(cores.primaryContainer, CoresDoApp.marca);
      expect(CoresDoApp.marca, const Color(0xFF615FF6));
    });

    test('o violeta da marca, e não verde, é a cor de ação', () {
      // Regressão do tema antigo (#1B7F3B). Se alguém trocar a semente sem
      // querer, o matiz sai da faixa do violeta e o teste acusa.
      final hsl = HSLColor.fromColor(cores.primary);
      expect(hsl.hue, greaterThan(230));
      expect(hsl.hue, lessThan(260));
    });
  });

  group('Fundo branco', () {
    test('a superfície de página é branco puro, não lilás tonal', () {
      // Pedido do dono em 07/08. Por padrão o M3 tinge a superfície com a
      // semente e o fundo sairia #FCF8FF.
      expect(cores.surface, Colors.white);
      expect(temaDoApp().scaffoldBackgroundColor, Colors.white);
      expect(temaDoApp().appBarTheme.backgroundColor, Colors.white);
    });

    test('a barra de baixo e o diálogo continuam se distinguindo da página', () {
      // Superfície elevada mantém o tom tonal de propósito: se tudo vira branco,
      // o diálogo perde a borda visual e a navegação se funde com a lista.
      expect(cores.surfaceContainer, isNot(Colors.white));
      expect(cores.surfaceContainerHigh, isNot(Colors.white));
    });

    test('o texto continua legível sobre o branco', () {
      expect(_contraste(Colors.white, cores.onSurface), greaterThan(4.5));
      expect(
        _contraste(Colors.white, cores.onSurfaceVariant),
        greaterThan(4.5),
      );
      expect(_contraste(Colors.white, cores.error), greaterThan(4.5));
      // Contorno de campo de texto: mínimo 3.0, é componente e não texto.
      expect(_contraste(Colors.white, cores.outline), greaterThan(3.0));
    });
  });

  group('Contraste mínimo (WCAG AA)', () {
    test('texto sobre as cores de ação passa 4.5', () {
      expect(_contraste(cores.primary, cores.onPrimary), greaterThan(4.5));
      expect(
        _contraste(cores.primaryContainer, cores.onPrimaryContainer),
        greaterThan(4.5),
      );
      expect(_contraste(cores.surface, cores.onSurface), greaterThan(4.5));
      expect(
        _contraste(cores.surface, cores.onSurfaceVariant),
        greaterThan(4.5),
      );
    });

    test('erro é legível sobre a superfície', () {
      // Recusa de prazo e mensagem de placar inválido vivem nessa cor.
      expect(_contraste(cores.surface, cores.error), greaterThan(4.5));
      expect(_contraste(cores.error, cores.onError), greaterThan(4.5));
    });

    test('sucesso com texto branco passa 4.5', () {
      // `Colors.green.shade700`, que estava no código, dava 4.12 e reprovava.
      expect(_contraste(CoresDoApp.sucesso, Colors.white), greaterThan(4.5));
    });

    test('o troféu do campeão é distinguível sobre a superfície', () {
      // Ícone: o mínimo da WCAG é 3.0. O âmbar 700 anterior dava 2.04 — o
      // troféu quase desaparecia para quem tem baixa visão, justamente no item
      // que a pessoa quer mostrar no grupo.
      expect(_contraste(CoresDoApp.campeao, cores.surface), greaterThan(3.0));
    });
  });

  group('MarcaRaro', () {
    testWidgets('mostra a palavra, o produto e o ponto na cor da marca', (
      t,
    ) async {
      await t.pumpWidget(
        MaterialApp(
          theme: temaDoApp(),
          home: const Scaffold(body: MarcaRaro(produto: 'bolão')),
        ),
      );

      expect(find.text('raro'), findsOneWidget);
      expect(find.text('bolão'), findsOneWidget);

      final ponto = t
          .widgetList<Container>(find.byType(Container))
          .firstWhere(
            (c) => (c.decoration as BoxDecoration?)?.color == CoresDoApp.marca,
          );
      expect(ponto, isNotNull);
    });

    testWidgets('anuncia a marca de uma vez para o leitor de tela', (t) async {
      await t.pumpWidget(
        MaterialApp(
          theme: temaDoApp(),
          home: const Scaffold(body: MarcaRaro(produto: 'bolão')),
        ),
      );

      // Sem isto o leitor soletra a palavra e lê o ponto como widget vazio.
      expect(find.bySemanticsLabel('raro — bolão'), findsOneWidget);
    });

    testWidgets('cabe com fonte grande do sistema', (t) async {
      await t.pumpWidget(
        MaterialApp(
          theme: temaDoApp(),
          home: const MediaQuery(
            data: MediaQueryData(textScaler: TextScaler.linear(2)),
            child: Scaffold(body: MarcaRaro(produto: 'bolão')),
          ),
        ),
      );
      await t.pumpAndSettle();

      // Sem exceção de layout no pump = sem overflow.
      expect(t.takeException(), isNull);
    });
  });
}
