/**
 * Kernel de cálculo matemático da NBR 6123:2023 — versão refatorada.
 *
 * Agora consome as tabelas oficiais (Tabela 1, 3, 4, A, B) e fornece
 * também:
 *  - Cálculo de S₂ via Tabela 3 (interpolação log-linear)
 *  - Cálculo de S₂ para intervalos de tempo arbitrários (Anexo A)
 *  - Cálculo de S₃ conforme Pₘ e vida útil (Anexo B)
 *  - Função para pressão interna (sec. 6.3) — implementada em internal-pressure.ts
 */

export type TerrainCategory = 'I' | 'II' | 'III' | 'IV' | 'V';
export type StructureClass = 'A' | 'B' | 'C';

import { TABLE_1 } from './nbr-tables/table-1';
import { getS2FromTable, getS2FromFormula } from './nbr-tables/table-3';
import { getS3ByGroup } from './nbr-tables/table-4';
import { getS3ByPmAndLife, calculateS3Analytical } from './nbr-tables/table-b';
import {
  getS2WithInterval,
  getDynamicS2Params,
  type TimeInterval,
} from './nbr-tables/table-a';
import { applyRoughnessChange } from './wind-direction';
import { ZG_BY_CATEGORY } from './nbr-tables/table-5';
import type { TerrainCategory as TC, StructureClass as SC } from './nbr-tables/_reexport';

export interface GlobalWindResult {
  readonly structClass: StructureClass;
  readonly s2: number;
  readonly vk: number;
  readonly q: number;
}

/**
 * Determina a classe da edificação conforme sua maior dimensão em
 * planta ou fachada (sec. 5.3.2):
 *  - Classe A: maior dimensão ≤ 20 m
 *  - Classe B: 20 m < maior dimensão ≤ 50 m
 *  - Classe C: maior dimensão > 50 m
 */
export function determineStructureClass(largestDimension: number): StructureClass {
  if (largestDimension <= 20) return 'A';
  if (largestDimension <= 50) return 'B';
  return 'C';
}

/** S₂ via Tabela 3 (recomendado) */
export function calculateS2(
  z: number,
  category: TerrainCategory,
  structureClass: StructureClass,
): number {
  return getS2FromTable(z, category, structureClass);
}

/** S₂ via equação teórica (debug/checagens) */
export function calculateS2Formula(
  z: number,
  category: TerrainCategory,
  structureClass: StructureClass,
): number {
  return getS2FromFormula(z, category, structureClass);
}

/** S₂ para intervalo de tempo arbitrário (Anexo A) */
export function calculateS2WithInterval(
  z: number,
  category: TerrainCategory,
  intervalS: number,
): number {
  return getS2WithInterval(z, category, intervalS);
}

/** Velocidade característica Vₖ = V₀ · S₁ · S₂ · S₃ */
export function calculateVk(
  v0: number,
  s1: number,
  s2: number,
  s3: number,
): number {
  return Number((v0 * s1 * s2 * s3).toFixed(2));
}

/** Pressão dinâmica q (kN/m²) */
export function calculateDynamicPressure(vk: number): number {
  const q_N = 0.613 * Math.pow(vk, 2);
  const q_kN = q_N / 1000;
  return Number(q_kN.toFixed(4));
}

/** S₃ por grupo (1–5) */
export function calculateS3ByGroup(group: 1 | 2 | 3 | 4 | 5): number {
  return getS3ByGroup(group);
}

/** S₃ por Pₘ e vida útil (Anexo B) */
export function calculateS3ByPmAndLife(pm: number, lifeYears: number): number {
  return getS3ByPmAndLife(pm, lifeYears);
}

/** S₃ analítico (qualquer Pₘ e vida útil) */
export function calculateS3AnalyticalFn(pm: number, lifeYears: number): number {
  return calculateS3Analytical(pm, lifeYears);
}

/**
 * Cálculo global (q) consolidado.
 */
export function calculateGlobalWindData(
  v0: number,
  s1: number,
  s3: number,
  category: TerrainCategory,
  largestDimension: number,
  height: number,
): GlobalWindResult {
  const structClass = determineStructureClass(largestDimension);
  const s2 = calculateS2(height, category, structClass);
  const vk = calculateVk(v0, s1, s2, s3);
  const q = calculateDynamicPressure(vk);
  return { structClass, s2, vk, q };
}

/**
 * Cálculo global considerando mudança de rugosidade (sec. 5.5).
 * Requer categoria do entorno (far) e distância x da mudança.
 */
export function calculateGlobalWindDataWithRoughness(
  v0: number,
  s1: number,
  s3: number,
  nearCategory: TerrainCategory,
  farCategory: TerrainCategory,
  distance: number,
  largestDimension: number,
  height: number,
): GlobalWindResult & { z1: number; s2Effective: number } {
  const structClass = determineStructureClass(largestDimension);
  const params = new Map<TerrainCategory, { b: number; p: number; fr: number }>();
  for (const cat of ['I', 'II', 'III', 'IV', 'V'] as const) {
    params.set(cat, TABLE_1[cat][structClass]);
  }
  const trans = applyRoughnessChange(
    { near: nearCategory, far: farCategory, distance, height },
    params,
  );
  const vk = calculateVk(v0, s1, trans.s2, s3);
  const q = calculateDynamicPressure(vk);
  return {
    structClass,
    s2: trans.s2,
    vk,
    q,
    z1: trans.z1,
    s2Effective: trans.s2,
  };
}

export {
  TABLE_1,
  getS2FromTable,
  getS2FromFormula,
  getS3ByGroup,
  getS3ByPmAndLife,
  calculateS3Analytical,
  getS2WithInterval,
  getDynamicS2Params,
  applyRoughnessChange,
  ZG_BY_CATEGORY,
};

export type { TimeInterval, TC as TerrainCategoryReexport, SC as StructureClassReexport };