/**
 * Strategy para cilindros de seção circular (NBR 6123:2023, sec. 6.2.1).
 *
 * Casos cobertos:
 *  - Silos / reservatórios / chaminés (eixo vertical)
 *  - Tubulações aéreas (eixo horizontal)
 *  - Topo aberto (Cpi específico pela Tabela 13 / sec. 6.3.2.3)
 */

import { getCpeCylinder, reynoldsCylinder, isSupercritical } from '../nbr-tables/table-13';
import { computeCpiCylinderOpenTop } from '../internal-pressure';
import { clampCpi } from '../internal-pressure';

export type CylinderEndType = 'closed' | 'open-top' | 'open-bottom' | 'open-both';

export interface CylinderInput {
  /** Diâmetro (m) */
  d: number;
  /** Altura (m) */
  h: number;
  /** Velocidade característica Vk (m/s) */
  vk: number;
  /** Tipo de superfície */
  surface: 'rough' | 'smooth';
  /** Tipo de extremidade */
  endType: CylinderEndType;
  /** Cpi base (usado se fechado) */
  baseCpi?: number;
}

export interface CylinderPoint {
  angle: number;
  cpe: number;
  pressureKN_m2: number;
}

export interface CylinderResult {
  re: number;
  supercritical: boolean;
  hOverD: number;
  cpi: number;
  cpiNote: string;
  profile: CylinderPoint[];
  /** Força horizontal total por unidade de altura (kN/m) — integração numérica */
  forcePerHeightKN_m: number;
}

/** Integração numérica da força de arrasto em torno do cilindro */
function integrateCylinderForce(
  profile: CylinderPoint[],
  d: number,
): number {
  let total = 0;
  for (let i = 0; i < profile.length - 1; i++) {
    const a = profile[i];
    const b = profile[i + 1];
    const da = (b.angle - a.angle) * Math.PI / 180;
    const avg = (a.pressureKN_m2 + b.pressureKN_m2) / 2;
    const radius = d / 2;
    total += avg * da * radius;
  }
  return Number(total.toFixed(3));
}

export function calculateCylinder(input: CylinderInput): CylinderResult {
  const { d, h, vk, surface, endType } = input;
  const hOverD = h / d;
  const re = reynoldsCylinder(vk, d);
  const supercritical = isSupercritical(re);

  let cpi = input.baseCpi ?? 0;
  let cpiNote = 'Edição fechada — usando Cpi global.';
  if (endType === 'open-top') {
    cpi = clampCpi(computeCpiCylinderOpenTop(hOverD));
    cpiNote = `Topo aberto: Cpi = ${cpi} (sec. 6.3.2.3, h/d = ${hOverD.toFixed(2)}).`;
  } else if (endType === 'open-bottom') {
    cpi = -0.5;
    cpiNote = 'Base aberta: Cpi = −0,5 (conservador).';
  } else if (endType === 'open-both') {
    cpi = -0.7;
    cpiNote = 'Topo e base abertos: Cpi = −0,7 (conservador).';
  }

  const angles = [0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180];
  const profile: CylinderPoint[] = angles.map((angle) => {
    const cpe = getCpeCylinder(angle, hOverD, surface);
    const p = (0.613 * Math.pow(vk, 2) * (cpe - cpi)) / 1000; // kN/m²
    return { angle, cpe, pressureKN_m2: Number(p.toFixed(3)) };
  });

  const forcePerHeightKN_m = integrateCylinderForce(profile, d);

  return {
    re,
    supercritical,
    hOverD,
    cpi,
    cpiNote,
    profile,
    forcePerHeightKN_m,
  };
}