/**
 * Cargas lineares (kN/m) para software estrutural — M9.2
 *
 * Converte pressões superficiais (kN/m²) em cargas distribuídas lineares
 * (kN/m) que o engenheiro digita diretamente em software como Ftool,
 * SAP2000, Eberick, TQS, etc.
 *
 * Convenções:
 *  - `q` é a pressão dinâmica em kN/m² (NBR 6123:2023, sec. 4.2)
 *  - `Cpe` e `Cpi` são adimensionais
 *  - Pressão líquida: p = q · (Cpe − Cpi)  [kN/m²]
 *  - Carga linear: w = p · (espaçamento / cos θ para cobertura inclinada)  [kN/m]
 *
 * Origem (galpão típico com pórticos transversais):
 *  - Terças (purlin): barras longitudinais no telhado que recebem carga
 *    distribuída na projeção horizontal. Para telhado inclinado,
 *    decompor a carga em normal e tangencial ao plano.
 *  - Pilares (columns): barras verticais nas paredes laterais.
 *  - Reação de base: cortante e normal na base de cada pilar.
 *
 * Todas as funções retornam sinal positivo para pressão (empuxo) e
 * negativo para sucção, mantendo a convenção da norma.
 */

import type { WallCoefficients, RoofCoefficients } from './coefficients';

/**
 * Carga linear em uma terça do telhado.
 *
 * Para um telhado inclinado com inclinação θ, a carga distribuída
 * sobre a barra horizontal (terça) é:
 *   w = q · (Cpe − Cpi) · s · cos θ
 *
 * onde `s` é o espaçamento entre terças (medido na projeção horizontal).
 * O fator cos θ corrige a área inclinada para a área de influência
 * da barra horizontal.
 *
 * @param cpe    Coeficiente de pressão externa na zona da cobertura
 * @param cpi    Coeficiente de pressão interna
 * @param q      Pressão dinâmica [kN/m²]
 * @param s      Espaçamento entre terças [m] (projeção horizontal)
 * @param theta  Inclinação do telhado [graus]
 * @returns      Carga distribuída na terça [kN/m] (sinal: + empuxo, − sucção)
 */
export function getWindLoadOnRoof(
  cpe: number,
  cpi: number,
  q: number,
  s: number,
  thetaDeg: number,
): number {
  if (s < 0) throw new Error('Espaçamento entre terças deve ser ≥ 0');
  const thetaRad = (thetaDeg * Math.PI) / 180;
  const p = q * (cpe - cpi);
  return Number((p * s * Math.cos(thetaRad)).toFixed(4));
}

/**
 * Vetor de cargas lineares nas terças do telhado, por zona E/F/G/H/I/J.
 *
 * Cada valor é a carga distribuída [kN/m] que atua sobre uma terça
 * localizada naquela zona, considerando o espaçamento entre terças `s`.
 *
 * Para telhados duas águas simétricos (Tabela 7), zonas E e F ficam
 * na água a barlavento, G e H na água a sotavento. I e J são platibandas.
 */
export function getRoofLinearLoads(
  cpi: number,
  q: number,
  roofCpe: RoofCoefficients,
  s: number,
  thetaDeg: number,
): RoofLinearLoads {
  return {
    E: getWindLoadOnRoof(roofCpe.E, cpi, q, s, thetaDeg),
    F: getWindLoadOnRoof(roofCpe.F, cpi, q, s, thetaDeg),
    G: getWindLoadOnRoof(roofCpe.G, cpi, q, s, thetaDeg),
    H: getWindLoadOnRoof(roofCpe.H, cpi, q, s, thetaDeg),
    I: getWindLoadOnRoof(roofCpe.I, cpi, q, s, thetaDeg),
    J: getWindLoadOnRoof(roofCpe.J, cpi, q, s, thetaDeg),
  };
}

export interface RoofLinearLoads {
  E: number;
  F: number;
  G: number;
  H: number;
  I: number;
  J: number;
}

/**
 * Carga linear distribuída em um pilar.
 *
 * O pilar recebe pressão de uma parede. A carga linear é:
 *   w = q · (Cpe − Cpi) · espaçamento_entre_pilares
 *
 * Diferente do telhado, paredes são verticais, então não há correção
 * de cosseno — a pressão é aplicada diretamente.
 *
 * @param cpe      Coeficiente de pressão externa na zona da parede
 * @param cpi      Coeficiente de pressão interna
 * @param q        Pressão dinâmica [kN/m²]
 * @param spacing  Espaçamento entre pórticos principais [m]
 * @returns        Carga distribuída no pilar [kN/m]
 */
export function getWindLoadOnColumn(
  cpe: number,
  cpi: number,
  q: number,
  spacing: number,
): number {
  if (spacing < 0) throw new Error('Espaçamento entre pórticos deve ser ≥ 0');
  const p = q * (cpe - cpi);
  return Number((p * spacing).toFixed(4));
}

/**
 * Cargas lineares nos 4 pilares do galpão para uma direção de vento.
 *
 * Para vento a 0° (perpendicular à largura):
 *  - Pilar barlavento: zona A
 *  - Pilar sotavento:  zona D
 *  - Pilares laterais: zonas B (lado do Cpe positivo) e C
 *
 * Para vento a 90°: as zonas A↔C e B↔D trocam.
 *
 * @returns Cargas lineares por pilar em kN/m (sinal: + empuxo, − sucção)
 */
export interface ColumnLinearLoads {
  windward: number;
  leeward: number;
  sideA: number;
  sideB: number;
}

export function getColumnLinearLoads(
  cpi: number,
  q: number,
  wallCpe: WallCoefficients,
  frameSpacing: number,
  windAngle: 0 | 90,
): ColumnLinearLoads {
  // Para 0°, o vento bate na face 'b' (menor). Na NBR 6123, as faces 'b' são C e D.
  // Logo, C = barlavento, D = sotavento. A e B são as laterais.
  if (windAngle === 0) {
    return {
      windward: getWindLoadOnColumn(wallCpe.C, cpi, q, frameSpacing),
      leeward: getWindLoadOnColumn(wallCpe.D, cpi, q, frameSpacing),
      sideA: getWindLoadOnColumn(wallCpe.A, cpi, q, frameSpacing),
      sideB: getWindLoadOnColumn(wallCpe.B, cpi, q, frameSpacing),
    };
  }
  // Para 90°, o vento bate na face 'a' (maior). Faces 'a' são A e B.
  // Logo, A = barlavento, B = sotavento. C e D são as laterais.
  return {
    windward: getWindLoadOnColumn(wallCpe.A, cpi, q, frameSpacing),
    leeward: getWindLoadOnColumn(wallCpe.B, cpi, q, frameSpacing),
    sideA: getWindLoadOnColumn(wallCpe.C, cpi, q, frameSpacing),
    sideB: getWindLoadOnColumn(wallCpe.D, cpi, q, frameSpacing),
  };
}

/**
 * Reação na base de um pilar (esforço cortante horizontal + normal).
 *
 * O pilar recebe uma carga distribuída ao longo de sua altura.
 * A reação na base é:
 *   V (cortante) = w · h_pilar  [kN]
 *   N (normal)   = w · h_pilar / 2 em cada lateral (não se aplica aqui
 *                  porque w é paralelo ao plano da parede)
 *
 * Para o galpão típico (pé-direito h), considera-se o pilar como
 * uma barra vertical engastada na base e livre no topo, com carga
 * uniformemente distribuída:
 *   V_base = w · h
 *
 * Esta é uma estimativa simplificada — casos com continuidade nos
 * nós do pórtico devem ser calculados pelo software estrutural.
 *
 * @param loadLinear   Carga distribuída no pilar [kN/m]
 * @param pillarHeight Altura do pilar [m]
 * @returns            Cortante na base [kN]
 */
export function getPillarBaseReaction(
  loadLinear: number,
  pillarHeight: number,
): number {
  if (pillarHeight < 0) throw new Error('Altura do pilar deve ser ≥ 0');
  return Number((loadLinear * pillarHeight).toFixed(4));
}

/**
 * Reações na base dos 4 pilares (cortante horizontal, sentido do vento).
 *
 * Útil para verificação rápida do pórtico transversal. Cada pilar tem
 * reação = w · h_pilar; somando os 4 obtém-se a reação total na base
 * do galpão (que deve estar em equilíbrio com a força de arrasto).
 */
export interface PillarBaseReactions {
  windward: number;
  leeward: number;
  sideA: number;
  sideB: number;
  total: number;
}

export function getAllPillarBaseReactions(
  columnLoads: ColumnLinearLoads,
  pillarHeight: number,
): PillarBaseReactions {
  const w = getPillarBaseReaction(columnLoads.windward, pillarHeight);
  const l = getPillarBaseReaction(columnLoads.leeward, pillarHeight);
  const a = getPillarBaseReaction(columnLoads.sideA, pillarHeight);
  const b = getPillarBaseReaction(columnLoads.sideB, pillarHeight);
  return { windward: w, leeward: l, sideA: a, sideB: b, total: w + l + a + b };
}

/**
 * Momento na base do pilar (para estimativa de fundação).
 *
 * Para pilar em balanço com carga uniformemente distribuída:
 *   M_base = w · h² / 2
 *
 * @returns Momento fletor na base [kN·m]
 */
export function getPillarBaseMoment(loadLinear: number, pillarHeight: number): number {
  if (pillarHeight < 0) throw new Error('Altura do pilar deve ser ≥ 0');
  return Number((loadLinear * pillarHeight * pillarHeight / 2).toFixed(4));
}

/**
 * Força de arrasto total no galpão (verificação global).
 *
 * Somatório das forças horizontais em todas as superfícies (paredes
 * paralelas ao vento desconsideradas conforme NBR 6123:2023 sec. 6.1):
 *   F_arrasto = q · (Σ Cpe · A − Cpi · A_total)  [kN]
 *
 * Esta é uma estimativa; a forma rigorosa usa as zonas detalhadas
 * de cada face (vide também `coefficients.ts`).
 */
export function getDragForce(
  wallCpe: WallCoefficients,
  roofCpe: RoofCoefficients,
  q: number,
  a: number, // comprimento (dimensão a da NBR, ao longo do eixo Z)
  b: number, // largura (dimensão b da NBR, ao longo do eixo X)
  h: number,
  thetaDeg: number,
  windAngle: 0 | 90 = 0,
): { forceKN: number; areaTotalM2: number; caEfetivo: number } {
  const thetaRad = (thetaDeg * Math.PI) / 180;
  const roofHeight = (b / 2) * Math.tan(thetaRad);

  let frontalArea = 0;
  let forceX = 0;

  if (windAngle === 0) {
    // Vento perpendicular à face b (largura). Face barlavento é a parede C, sotavento é parede D.
    // O comprimento b define as empenas. A área da parede retangular é b * h.
    // Mas wait, se o vento é perpendicular a b, a fachada que recebe o vento tem dimensão b. 
    // Então a área é b * h.
    frontalArea = b * h;
    
    const Cpe_w = wallCpe.C;
    const Cpe_l = wallCpe.D;
    
    // Força nas paredes = (Cpe_w - Cpi) * A - (Cpe_l - Cpi) * (-A) = (Cpe_w - Cpe_l) * A
    const F_walls = q * (Cpe_w - Cpe_l) * frontalArea;
    
    // No telhado, a 0°, o vento bate na empena do telhado (triângulo se for fechado).
    // Mas a NBR 6123 assume que 0° bate paralelo à cumeeira?
    // Não, a convenção do app: 0° perpendicular à largura (b), 90° paralelo à largura.
    // Zonas E, F, G, H são águas do telhado (para 90°, incidem sobre as águas laterais).
    // Para 0°, o vento corre *paralelo* às águas, gerando arrasto por atrito.
    // Simplificando, para 0°, as faces frontais E e G (ou placa de empena) seriam o arrasto.
    forceX = F_walls; // Ignorando o triângulo da empena para cálculo simplificado
  } else {
    // Vento perpendicular à face a (comprimento). Face a = A (barlavento), B (sotavento).
    frontalArea = a * h;
    const Cpe_w = wallCpe.A;
    const Cpe_l = wallCpe.B;
    
    const F_walls = q * (Cpe_w - Cpe_l) * frontalArea;
    
    // Telhado a 90°: águas E/F (barlavento) e G/H (sotavento).
    // Projeção frontal de E/F é (a * roofHeight). Como é força horizontal, multiplicamos pelo seno.
    // Área da face inclinada = a * (b/2)/cos. Força normal = q * Cpe * A_inclinada.
    // Componente X = F_n * sin(theta) = q * Cpe * A_inclinada * sin(theta)
    // A_inclinada * sin(theta) = (a * b / (2*cos(theta))) * sin(theta) = a * (b/2) * tan(theta) = A_roof_frontal_90
    
    // Média do Cpe na água a barlavento (E e F) e sotavento (G e H)
    const Cpe_roof_w = (roofCpe.E + roofCpe.F) / 2;
    const Cpe_roof_l = (roofCpe.G + roofCpe.H) / 2;
    
    const F_roof = q * (Cpe_roof_w - Cpe_roof_l) * (a * roofHeight);
    
    forceX = F_walls + F_roof;
  }

  const caEfetivo = forceX / (q * frontalArea);

  return {
    forceKN: Number(forceX.toFixed(4)),
    areaTotalM2: Number(frontalArea.toFixed(2)),
    caEfetivo: Number(caEfetivo.toFixed(3)),
  };
}