/**
 * Testes de auditoria M9.1 — valores amostrais de cada tabela da NBR 6123:2023.
 *
 * Estes testes confirmam que os valores retornados pelas funções correspondem
 * aos valores oficiais da norma (com pequena tolerância numérica).
 */

import { describe, it, expect } from 'vitest';

import { TABLE_1, ZG_BY_CATEGORY } from '../nbr-tables/table-1';
import { TABLE_4, getS3ByGroup, getS3VidaUtilByGroup } from '../nbr-tables/table-4';
import { Z0_BY_CATEGORY } from '../nbr-tables/table-5';
import { getS2FromTable } from '../nbr-tables/table-3';
import { TABLE_32 } from '../nbr-tables/table-32';
import { BRIDGE_DAMPING, getBridgeParams } from '../nbr-tables/table-35';
import { METEOROLOGICAL_STATIONS, getStationById } from '../nbr-tables/stations';
import { calculateS3Analytical } from '../nbr-tables/table-b';

describe('M9.1 — Tabela 1 (Parâmetros meteorológicos)', () => {
  it('Cat. II, Classe A → b=1,00; p=0,085; Fr=1,00', () => {
    expect(TABLE_1.II.A.b).toBe(1.0);
    expect(TABLE_1.II.A.p).toBe(0.085);
    expect(TABLE_1.II.A.fr).toBe(1.0);
  });

  it('Cat. V, Classe C → b=0,71; p=0,175; Fr=0,95', () => {
    expect(TABLE_1.V.C.b).toBe(0.71);
    expect(TABLE_1.V.C.p).toBe(0.175);
    expect(TABLE_1.V.C.fr).toBe(0.95);
  });

  it('Todas as 5 categorias e 3 classes presentes', () => {
    for (const cat of ['I', 'II', 'III', 'IV', 'V'] as const) {
      for (const cls of ['A', 'B', 'C'] as const) {
        expect(TABLE_1[cat][cls]).toBeDefined();
      }
    }
  });
});

describe('M9.1 — Tabela 4 (Valores mínimos de S3)', () => {
  it('Grupo 1 → S3 = 1,11 (NBR 6123:2023 p. 15)', () => {
    expect(getS3ByGroup(1)).toBe(1.11);
  });

  it('Grupo 2 → S3 = 1,06 (NBR 6123:2023 p. 15)', () => {
    expect(getS3ByGroup(2)).toBe(1.06);
  });

  it('Grupo 3 → S3 = 1,00', () => {
    expect(getS3ByGroup(3)).toBe(1.0);
  });

  it('Grupo 4 → S3 = 0,95', () => {
    expect(getS3ByGroup(4)).toBe(0.95);
  });

  it('Grupo 5 → S3 = 0,83', () => {
    expect(getS3ByGroup(5)).toBe(0.83);
  });

  it('Vida útil por grupo (NBR 6123:2023 Tabela 4)', () => {
    expect(getS3VidaUtilByGroup(1)).toBe(100);
    expect(getS3VidaUtilByGroup(2)).toBe(75);
    expect(getS3VidaUtilByGroup(3)).toBe(50);
    expect(getS3VidaUtilByGroup(4)).toBe(30);
    expect(getS3VidaUtilByGroup(5)).toBe(2);
  });

  it('Pₘ = 0,63 consistente para todos os grupos', () => {
    for (const g of TABLE_4) {
      expect(g.pm).toBe(0.63);
    }
  });
});

describe('M9.1 — Tabela 5 (z_g e z_0)', () => {
  it('Cat. I → z_g=250 m; z_0=0,005 m', () => {
    expect(ZG_BY_CATEGORY.I).toBe(250);
    expect(Z0_BY_CATEGORY.I).toBe(0.005);
  });

  it('Cat. II → z_g=300 m; z_0=0,07 m', () => {
    expect(ZG_BY_CATEGORY.II).toBe(300);
    expect(Z0_BY_CATEGORY.II).toBe(0.07);
  });

  it('Cat. V → z_g=500 m; z_0=2,5 m', () => {
    expect(ZG_BY_CATEGORY.V).toBe(500);
    expect(Z0_BY_CATEGORY.V).toBe(2.5);
  });
});

describe('M9.1 — Tabela 3 (Fator S2)', () => {
  it('Cat. II, Classe A, z=10 m → S2 ≈ 1,00', () => {
    expect(getS2FromTable(10, 'II', 'A')).toBeCloseTo(1.0, 2);
  });

  it('Cat. I, Classe A, z=10 m → S2 ≈ 1,10', () => {
    expect(getS2FromTable(10, 'I', 'A')).toBeCloseTo(1.1, 2);
  });

  it('Saturação em z_g: z=1000 m não cresce indefinidamente', () => {
    const s2_catI = getS2FromTable(1000, 'I', 'A');
    const s2_zg = getS2FromTable(250, 'I', 'A');
    expect(s2_catI).toBe(s2_zg);
  });

  it('Limite inferior: z < 5 m é tratado como z = 5 m', () => {
    expect(getS2FromTable(1, 'II', 'A')).toBe(getS2FromTable(5, 'II', 'A'));
  });
});

describe('M9.1 — Tabela 32 (Expoente p e bₘ dinâmicos)', () => {
  it('Cat. I → p=0,095; bₘ=1,23 (NBR 6123:2023 p. 63)', () => {
    expect(TABLE_32.I.p).toBe(0.095);
    expect(TABLE_32.I.bm).toBe(1.23);
  });

  it('Cat. V → p=0,31; bₘ=0,50', () => {
    expect(TABLE_32.V.p).toBe(0.31);
    expect(TABLE_32.V.bm).toBe(0.5);
  });

  it('p cresce com a categoria (mais rugoso)', () => {
    const cats = ['I', 'II', 'III', 'IV', 'V'] as const;
    for (let i = 1; i < cats.length; i++) {
      expect(TABLE_32[cats[i]].p).toBeGreaterThanOrEqual(TABLE_32[cats[i - 1]].p);
    }
  });

  it('bₘ decresce com a categoria', () => {
    const cats = ['I', 'II', 'III', 'IV', 'V'] as const;
    for (let i = 1; i < cats.length; i++) {
      expect(TABLE_32[cats[i]].bm).toBeLessThanOrEqual(TABLE_32[cats[i - 1]].bm);
    }
  });
});

describe('M9.1 — Tabela 35 (Parâmetros para pontes)', () => {
  it('Cat. I → p=0,10; bₘ=1,25 (constantes por categoria, NBR 6123:2023 p. 80)', () => {
    const { b, p } = getBridgeParams(15, 'I');
    expect(p).toBe(0.1);
    expect(b).toBe(1.25);
  });

  it('Cat. II → p=0,16; bₘ=1,00', () => {
    const { b, p } = getBridgeParams(30, 'II');
    expect(p).toBe(0.16);
    expect(b).toBe(1.0);
  });

  it('Cat. V → p=0,35; bₘ=0,44', () => {
    const { b, p } = getBridgeParams(50, 'V');
    expect(p).toBe(0.35);
    expect(b).toBe(0.44);
  });

  it('Valores não variam com z (Tabela 35 é por categoria, não por altura)', () => {
    const z10 = getBridgeParams(10, 'III');
    const z80 = getBridgeParams(80, 'III');
    expect(z10.b).toBe(z80.b);
    expect(z10.p).toBe(z80.p);
  });
});

describe('M9.1 — Tabela 36 (Taxas de amortecimento de pontes)', () => {
  it('Aço soldadas, pav. asfáltico → ξ = 0,8%', () => {
    const entry = BRIDGE_DAMPING.find((e) => e.detail.includes('asfáltico'));
    expect(entry?.xiPercent).toBe(0.8);
  });

  it('Concreto armado → ξ = 2,5%', () => {
    const entry = BRIDGE_DAMPING.find((e) => e.material === 'Concreto armado');
    expect(entry?.xiPercent).toBe(2.5);
  });

  it('Madeira → ξ = 8,0% (NBR 6123:2023 p. 84)', () => {
    const entry = BRIDGE_DAMPING.find((e) => e.material === 'Madeira');
    expect(entry?.xiPercent).toBe(8.0);
  });

  it('Material compósito → ξ = 6,0%', () => {
    const entry = BRIDGE_DAMPING.find((e) => e.material === 'Material compósito');
    expect(entry?.xiPercent).toBe(6.0);
  });
});

describe('M9.1 — Anexo C (Estações meteorológicas)', () => {
  it('49 estações cadastradas', () => {
    expect(METEOROLOGICAL_STATIONS).toHaveLength(49);
  });

  it('Curitiba (id=13) altitude 910 m (corrigido do valor antigo 510 m)', () => {
    const cwb = getStationById(13);
    expect(cwb?.nome).toBe('Curitiba');
    expect(cwb?.altitude).toBe(910);
  });

  it('Belo Horizonte (id=5) altitude 789 m', () => {
    const bh = getStationById(5);
    expect(bh?.altitude).toBe(789);
  });

  it('Anápolis (id=2) altitude 1097 m', () => {
    const ana = getStationById(2);
    expect(ana?.altitude).toBe(1097);
  });

  it('Porto Alegre (id=32) altitude 4 m, V₀=45 m/s', () => {
    const poa = getStationById(32);
    expect(poa?.altitude).toBe(4);
    expect(poa?.v0).toBe(45);
  });

  it('Florianópolis (id=18) V₀=45 m/s (Sul)', () => {
    const flo = getStationById(18);
    expect(flo?.v0).toBe(45);
  });

  it('Cada estação tem coordenadas, altitude e V₀ definidos', () => {
    for (const s of METEOROLOGICAL_STATIONS) {
      expect(s.latitude).toBeTruthy();
      expect(s.longitude).toBeTruthy();
      expect(s.altitude).toBeGreaterThanOrEqual(0);
      expect(s.v0).toBeGreaterThan(0);
    }
  });
});

describe('M9.1 — Anexo B (Fator S3 analítico)', () => {
  it('S3(0,63, 50) ≈ 0,95 (analítico; Tabela B.1 usa valores pré-computados)', () => {
    const s3 = calculateS3Analytical(0.63, 50);
    expect(s3).toBeCloseTo(0.95, 1);
  });

  it('S3(0,63, 25) ≈ 0,89', () => {
    const s3 = calculateS3Analytical(0.63, 25);
    expect(s3).toBeCloseTo(0.89, 1);
  });

  it('S3(0,63, 2) ≈ 0,57', () => {
    const s3 = calculateS3Analytical(0.63, 2);
    expect(s3).toBeCloseTo(0.57, 1);
  });

  it('S3 aumenta com vida útil (mantida Pₘ fixa)', () => {
    const s3_2 = calculateS3Analytical(0.63, 2);
    const s3_50 = calculateS3Analytical(0.63, 50);
    const s3_200 = calculateS3Analytical(0.63, 200);
    expect(s3_200).toBeGreaterThan(s3_50);
    expect(s3_50).toBeGreaterThan(s3_2);
  });

  it('S3 DIMINUI com Pₘ (mantida vida útil fixa) — mais Pₘ = rajadas menos raras', () => {
    const s3_p10 = calculateS3Analytical(0.1, 50);
    const s3_p90 = calculateS3Analytical(0.9, 50);
    expect(s3_p90).toBeLessThan(s3_p10);
  });

  it('Rejeita Pₘ fora de (0,1)', () => {
    expect(() => calculateS3Analytical(0, 50)).toThrow();
    expect(() => calculateS3Analytical(1, 50)).toThrow();
  });

  it('Rejeita vida útil ≤ 0', () => {
    expect(() => calculateS3Analytical(0.5, 0)).toThrow();
    expect(() => calculateS3Analytical(0.5, -1)).toThrow();
  });
});