/**
 * Strategy — Pontes (NBR 6123:2023, sec. 11).
 *
 * Inclui:
 *  - Cálculo do parâmetro de susceptibilidade Pse (sec. 11.2.2)
 *  - Classificação Classe 1/2/3
 *  - Coeficientes Cx (drag) e Cz (lift) do tabuleiro (sec. 11.3.2 e 11.3.3)
 *  - Velocidade V_it = 0,65 · Vo · S1 · b · (z/10)^p
 */

import { getBridgeParams } from '../nbr-tables/table-35';
import type { TerrainCategory } from '../wind-kernel';

export interface BridgeClassificationInput {
  /** Maior vão Lp (m) */
  lp: number;
  /** Largura do tabuleiro B (m) */
  width: number;
  /** Massa por unidade de comprimento m (kg/m) */
  massPerLength: number;
  /** Frequência do 1º modo de flexão vertical f_v (Hz) */
  fv: number;
  /** Velocidade básica Vo (m/s) */
  v0: number;
  /** S1 */
  s1: number;
  /** Altura z do tabuleiro (m) */
  deckHeight: number;
  /** Categoria do terreno */
  category: TerrainCategory;
}

export type BridgeClass = 1 | 2 | 3;

export interface BridgeClassificationResult {
  pse: number;
  vit: number;
  bridgeClass: BridgeClass;
  description: string;
}

/**
 * Parâmetro de susceptibilidade aerodinâmica:
 *   Pse = ρ · B² / (m · f_v · Lp²) · (V_it / B)²
 *
 * Simplificado (sec. 11.2.2):
 *   Pse = ρ · V_it² / (m · f_v²)
 */
export function classifyBridge(input: BridgeClassificationInput): BridgeClassificationResult {
  const { lp, width, massPerLength, fv, v0, s1, deckHeight, category } = input;
  const { b, p } = getBridgeParams(deckHeight, category);
  const vit = 0.65 * v0 * s1 * b * Math.pow(deckHeight / 10, p);

  const rho = 1.226;
  // Forma simplificada da norma
  const pse = (rho * vit * vit * lp * lp) / (massPerLength * fv * fv * width * width);

  let bridgeClass: BridgeClass;
  let description: string;
  if (pse < 0.04) {
    bridgeClass = 1;
    description = 'Classe 1: efeitos dinâmicos podem ser desconsiderados.';
  } else if (pse <= 1.0) {
    bridgeClass = 2;
    description = 'Classe 2: efeitos dinâmicos devem ser avaliados.';
  } else {
    bridgeClass = 3;
    description = 'Classe 3: ponte muito susceptível — análise aeroelástica requerida.';
  }

  return {
    pse: Number(pse.toFixed(4)),
    vit: Number(vit.toFixed(2)),
    bridgeClass,
    description,
  };
}

export interface BridgeDeckForcesInput {
  /** Largura do tabuleiro B (m) */
  width: number;
  /** Altura equivalente Heg (m) — soma das áreas expostas por unidade de comprimento */
  heg: number;
  /** Velocidade característica Vk(z) (m/s) */
  vk: number;
  /** Pressão dinâmica q (kN/m²) */
  q: number;
  /** Ângulo de ataque do vento (graus) */
  alpha?: number;
}

export interface BridgeDeckForcesResult {
  /** Coeficiente de arrasto Cx */
  cx: number;
  /** Coeficiente de sustentação Cz */
  cz: number;
  /** Coeficiente de momento torcional Cm */
  cm: number;
  /** Fx = q · B · Cx (kN/m) */
  fxPerLength: number;
  /** Fz = q · B · Cz (kN/m) */
  fzPerLength: number;
  /** Fm = q · B² · Cm (kNm/m) */
  fmPerLength: number;
}

/**
 * Coeficientes de força do tabuleiro (sec. 11.3.2 e 11.3.3):
 *   Cx = 0,21 + 1,8304 · (B / Heg)^(-1,1267)  se 1 ≤ B/Heg ≤ 27
 *   Cz = -0,0428 · (B/Heg)² + 0,7472
 *   Variação típica: |Cz| ≤ 1,0
 */
export function calculateBridgeDeckForces(input: BridgeDeckForcesInput): BridgeDeckForcesResult {
  const { width, heg, q, alpha = 0 } = input;
  const ratio = width / heg;
  
  let cx0: number;
  if (ratio < 1) {
    cx0 = 2.0;
  } else if (ratio > 27) {
    cx0 = 0.21 + 1.8304 * Math.pow(ratio, -1.1267);
  } else {
    cx0 = 0.21 + 1.8304 * Math.pow(ratio, -1.1267);
  }
  const czRaw0 = -0.0428 * ratio * ratio + 0.7472;
  const czBase = Math.abs(czRaw0) > 1.0 ? Math.sign(czRaw0) * 1.0 : czRaw0;

  // Efeito do ângulo de ataque
  const alphaRad = (alpha * Math.PI) / 180;
  const dCz_da = 3.0; // rad^-1
  const dCm_da = 0.8; // rad^-1

  const cxRaw = cx0 * (1 + 0.03 * Math.abs(alpha));
  const czRaw = czBase + dCz_da * alphaRad;
  
  // Cm base ≈ 0.1 * Cz0 (excentricidade) + contribuição do ângulo de ataque
  const cmRaw = (czBase * 0.1) + dCm_da * alphaRad;

  const cz = Math.abs(czRaw) > 1.5 ? Math.sign(czRaw) * 1.5 : czRaw;
  const cx = cxRaw;
  const cm = cmRaw;

  const fxPerLength = Number((q * width * cx).toFixed(3));
  const fzPerLength = Number((q * width * cz).toFixed(3));
  const fmPerLength = Number((q * width * width * cm).toFixed(3));

  return { 
    cx: Number(cx.toFixed(3)), 
    cz: Number(cz.toFixed(3)), 
    cm: Number(cm.toFixed(3)),
    fxPerLength, 
    fzPerLength,
    fmPerLength
  };
}

export interface StabilityResult {
  ok: boolean;
  vf: number;
  vkCrit: number;
}

/** Verificação contra flutter: Vcr > 2,0 · Vk (sec. 11.5.4) */
export function flutterCheck(vf: number, vk: number): StabilityResult {
  const vkCrit = 2.0 * vk;
  return { ok: vf > vkCrit, vf, vkCrit };
}

/** Verificação contra galope: Vcr > 1.25 · Vk (sec. 11.5.6) */
export function gallopingCheck(vg: number, vk: number): StabilityResult {
  const vkCrit = 1.25 * vk;
  return { ok: vg > vkCrit, vf: vg, vkCrit };
}