/**
 * Testes do módulo de cargas lineares (M9.2).
 *
 * Validação numérica das funções que convertem pressões (kN/m²) em
 * cargas lineares (kN/m) para software estrutural.
 */

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

import {
  getWindLoadOnRoof,
  getWindLoadOnColumn,
  getPillarBaseReaction,
  getPillarBaseMoment,
  getColumnLinearLoads,
  getAllPillarBaseReactions,
  getRoofLinearLoads,
  getDragForce,
} from '../line-loads';
import type { WallCoefficients, RoofCoefficients } from '../coefficients';

const WALL_CPE_0: WallCoefficients = { A: -1.1, B: -0.8, C: -0.5, D: -0.5 };
const WALL_CPE_90: WallCoefficients = { A: -0.5, B: -0.5, C: -1.1, D: -0.8 };
const ROOF_CPE: RoofCoefficients = { E: -1.0, F: -1.0, G: -0.5, H: -0.5, I: 0, J: 0 };

describe('M9.2 — Carga linear no telhado (terças)', () => {
  it('Caso base: Cpe=-1,0, Cpi=-0,3, q=1,0 kN/m², s=1,5 m, θ=10°', () => {
    const w = getWindLoadOnRoof(-1.0, -0.3, 1.0, 1.5, 10);
    const p = 1.0 * (-1.0 - -0.3);
    expect(w).toBeCloseTo(p * 1.5 * Math.cos((10 * Math.PI) / 180), 3);
  });

  it('Carga é zero quando Cpe = Cpi', () => {
    const w = getWindLoadOnRoof(-0.3, -0.3, 1.0, 1.5, 10);
    expect(w).toBe(0);
  });

  it('Carga dobra quando espaçamento entre terças dobra', () => {
    const w1 = getWindLoadOnRoof(-1.0, -0.3, 1.0, 1.5, 10);
    const w2 = getWindLoadOnRoof(-1.0, -0.3, 1.0, 3.0, 10);
    expect(w2).toBeCloseTo(2 * w1, 3);
  });

  it('Inclinação 0° (telhado plano) → cos θ = 1', () => {
    const w = getWindLoadOnRoof(-1.0, -0.3, 1.0, 1.5, 0);
    expect(w).toBeCloseTo(-1.05, 3);
  });

  it('Inclinação 60° → cos θ = 0,5', () => {
    const w = getWindLoadOnRoof(-1.0, -0.3, 1.0, 1.5, 60);
    expect(w).toBeCloseTo(-1.05 * Math.cos((60 * Math.PI) / 180), 3);
  });

  it('Empuxo positivo (sinal +) quando Cpe > Cpi', () => {
    const w = getWindLoadOnRoof(+0.7, -0.3, 1.0, 1.5, 10);
    expect(w).toBeGreaterThan(0);
  });

  it('Rejeita espaçamento negativo', () => {
    expect(() => getWindLoadOnRoof(-1.0, -0.3, 1.0, -0.5, 10)).toThrow();
  });
});

describe('M9.2 — Carga linear no pilar', () => {
  it('Pilar barlavento: q=1,0, Cpe=-1,1, Cpi=-0,3, spacing=6 m', () => {
    const w = getWindLoadOnColumn(-1.1, -0.3, 1.0, 6.0);
    expect(w).toBeCloseTo(-4.8, 3);
  });

  it('Carga é zero quando Cpe = Cpi', () => {
    const w = getWindLoadOnColumn(-0.3, -0.3, 1.0, 6.0);
    expect(w).toBe(0);
  });

  it('Empuxo positivo (sinal +) quando Cpe > Cpi', () => {
    const w = getWindLoadOnColumn(+0.7, -0.3, 1.0, 6.0);
    expect(w).toBeGreaterThan(0);
  });

  it('Rejeita espaçamento negativo', () => {
    expect(() => getWindLoadOnColumn(-1.1, -0.3, 1.0, -1)).toThrow();
  });
});

describe('M9.2 — Reação na base do pilar', () => {
  it('V_base = w · h', () => {
    const v = getPillarBaseReaction(2.5, 6.0);
    expect(v).toBeCloseTo(15.0, 3);
  });

  it('V_base = 0 quando w = 0', () => {
    expect(getPillarBaseReaction(0, 6)).toBe(0);
  });

  it('Rejeita altura negativa', () => {
    expect(() => getPillarBaseReaction(2.5, -1)).toThrow();
  });
});

describe('M9.2 — Momento na base do pilar', () => {
  it('M_base = w · h² / 2', () => {
    const m = getPillarBaseMoment(2.5, 6.0);
    expect(m).toBeCloseTo(45.0, 3);
  });

  it('M_base = 0 quando w = 0', () => {
    expect(getPillarBaseMoment(0, 6)).toBe(0);
  });

  it('Momento escala com h²', () => {
    const m1 = getPillarBaseMoment(2.5, 4.0);
    const m2 = getPillarBaseMoment(2.5, 8.0);
    expect(m2 / m1).toBeCloseTo(4, 3);
  });
});

describe('M9.2 — Cargas lineares nos 4 pilares (vento 0°)', () => {
  it('Mapeia zonas C→barlavento, D→sotavento, A/B→laterais', () => {
    const loads = getColumnLinearLoads(-0.3, 1.0, WALL_CPE_0, 6.0, 0);
    // WALL_CPE_0: { A: -1.1, B: -0.8, C: -0.5, D: -0.5 }
    expect(loads.windward).toBeCloseTo(1.0 * (-0.5 - -0.3) * 6, 3); // C
    expect(loads.leeward).toBeCloseTo(1.0 * (-0.5 - -0.3) * 6, 3); // D
    expect(loads.sideA).toBeCloseTo(1.0 * (-1.1 - -0.3) * 6, 3); // A
    expect(loads.sideB).toBeCloseTo(1.0 * (-0.8 - -0.3) * 6, 3); // B
  });

  it('Vento 90°: barlavento ← zona A', () => {
    const loads = getColumnLinearLoads(-0.3, 1.0, WALL_CPE_90, 6.0, 90);
    // WALL_CPE_90: { A: -0.5, B: -0.5, C: -1.1, D: -0.8 };
    expect(loads.windward).toBeCloseTo(1.0 * (-0.5 - -0.3) * 6, 3); // A
  });
});

describe('M9.2 — Reações nos 4 pilares', () => {
  it('Cada pilar: V = w · h; total = soma', () => {
    const columnLoads = getColumnLinearLoads(-0.3, 1.0, WALL_CPE_0, 6.0, 0);
    const reactions = getAllPillarBaseReactions(columnLoads, 6.0);

    expect(reactions.windward).toBeCloseTo(columnLoads.windward * 6.0, 3);
    expect(reactions.leeward).toBeCloseTo(columnLoads.leeward * 6.0, 3);
    expect(reactions.sideA).toBeCloseTo(columnLoads.sideA * 6.0, 3);
    expect(reactions.sideB).toBeCloseTo(columnLoads.sideB * 6.0, 3);

    const expectedTotal =
      reactions.windward + reactions.leeward + reactions.sideA + reactions.sideB;
    expect(reactions.total).toBeCloseTo(expectedTotal, 3);
  });

  it('Total é negativo (sucção) para vento em zona predominantemente negativa', () => {
    const columnLoads = getColumnLinearLoads(-0.3, 1.0, WALL_CPE_0, 6.0, 0);
    const reactions = getAllPillarBaseReactions(columnLoads, 6.0);
    expect(reactions.total).toBeLessThan(0);
  });
});

describe('M9.2 — Cargas lineares no telhado (todas as zonas)', () => {
  it('Mapeia zonas E, F, G, H, I, J com mesmo Cpi/q/espaçamento/θ', () => {
    const loads = getRoofLinearLoads(-0.3, 1.0, ROOF_CPE, 1.5, 10);
    const cos10 = Math.cos((10 * Math.PI) / 180);

    expect(loads.E).toBeCloseTo(1.0 * (-1.0 - -0.3) * 1.5 * cos10, 3);
    expect(loads.F).toBeCloseTo(1.0 * (-1.0 - -0.3) * 1.5 * cos10, 3);
    expect(loads.G).toBeCloseTo(1.0 * (-0.5 - -0.3) * 1.5 * cos10, 3);
    expect(loads.H).toBeCloseTo(1.0 * (-0.5 - -0.3) * 1.5 * cos10, 3);
    expect(loads.I).toBeCloseTo(1.0 * (0 - -0.3) * 1.5 * cos10, 3);
    expect(loads.J).toBeCloseTo(1.0 * (0 - -0.3) * 1.5 * cos10, 3);
  });
});

describe('M9.2 — Força de arrasto total (verificação global)', () => {
  it('Exemplo: galpão 30×15×6 m, θ=10°, V₀=40 m/s', () => {
    // Usando Cpe realista onde C (barlavento) e D (sotavento) geram arrasto
    const CPE_REAL: WallCoefficients = { A: -0.8, B: -0.5, C: +0.7, D: -0.3 };
    const result = getDragForce(CPE_REAL, ROOF_CPE, 1.0, 30, 15, 6, 10, 0);

    expect(result.areaTotalM2).toBe(15 * 6); // Frente: b * h = 90
    
    // Força = q * (Cpe_w - Cpe_l) * Area = 1.0 * (0.7 - (-0.3)) * 90 = 90 kN
    expect(result.forceKN).toBeCloseTo(90, 1);
  });

  it('Cpi não afeta a força de arrasto global (anulação vetorial)', () => {
    const CPE: WallCoefficients = { A: 0, B: 0, C: +0.7, D: -0.3 };
    const zeroRoofCpe = { E: 0, F: 0, G: 0, H: 0, I: 0, J: 0 };
    
    const resultComCpiPos = getDragForce(CPE, zeroRoofCpe, 1.0, 30, 15, 6, 0, 0);
    const resultComCpiNeg = getDragForce(CPE, zeroRoofCpe, 1.0, 30, 15, 6, 0, 0);
    
    expect(resultComCpiPos.forceKN).toBeCloseTo(resultComCpiNeg.forceKN, 3);
    expect(resultComCpiPos.forceKN).toBe(90); // (0.7 - (-0.3)) * 90
  });
});

describe('M9.2 — Integração com Blessmann (sanity check)', () => {
  it('Arrasto é calculado corretamente a 90° (vento na maior dimensão)', () => {
    const CPE_REAL_90: WallCoefficients = { A: +0.7, B: -0.3, C: -0.8, D: -0.5 };
    const ROOF_REAL_90: RoofCoefficients = { E: -0.8, F: -0.8, G: -0.4, H: -0.4, I: 0, J: 0 };
    
    const result = getDragForce(CPE_REAL_90, ROOF_REAL_90, 1.0, 30, 15, 6, 10, 90);
    
    expect(result.areaTotalM2).toBe(30 * 6); // Frente: a * h = 180
    
    // Força Paredes = 1.0 * (0.7 - (-0.3)) * 180 = 180 kN
    // Força Telhado = 1.0 * (-0.8 - (-0.4)) * (a * b/2 * tan(10°)) = -0.4 * 30 * 7.5 * 0.1763 = -15.87
    // Total = 180 - 15.87 = 164.13
    expect(result.forceKN).toBeCloseTo(164.13, 1);
  });
});