/**
 * Módulo de Ações de Vento para Abrigos, Marquises e Pórticos com Fechamentos Parciais
 * NBR 6123:2023 — Combinação de Coberturas Isoladas (Tab. 24 e 25), Pressão Interna e Aberturas (sec. 6.3)
 * e Placas / Painéis (Tab. 23), suportando incidências do vento a 0°, 45° e 90°.
 */

export type ShelterCondition = 'cond1' | 'cond2' | 'cond3' | 'cond4';
export type OpeningPosition = 'top' | 'bottom' | 'distributed';
export type ShelterWindAngle = 0 | 45 | 90;

export interface ShelterInput {
  /** Condição topológica padrão (1=Estanque 3 lados, 2=Fundo fechado, 3=Túnel, 4=Aberto) */
  condition: ShelterCondition;
  /** Comprimento do balanço / profundidade (m) */
  depth: number;
  /** Largura do abrigo (m) */
  width: number;
  /** Altura livre / altura do pilar (m) */
  height: number;
  /** Inclinação da cobertura (graus, ex: 0 a 30) */
  theta: number;
  /** Percentual de fechamento da parede de fundo (0 a 100) */
  backClosure: number;
  /** Faixa de fechamento da parede de fundo [base%, topo%] (0 a 100) */
  backRange?: [number, number];
  /** Percentual de fechamento da parede lateral esquerda (0 a 100) */
  leftClosure: number;
  /** Percentual de fechamento da parede lateral direita (0 a 100) */
  rightClosure: number;
  /** Posição da abertura na parede de fundo ('top' | 'bottom' | 'distributed') */
  openingPos: OpeningPosition;
  /** Ângulo de incidência do vento (0° = frontal, 45° = oblíquo, 90° = lateral) */
  windAngle?: ShelterWindAngle;
}

export interface ShelterResult {
  /** Ângulo de incidência do vento calculado (0, 45 ou 90) */
  windAngle: ShelterWindAngle;
  /** Coeficiente de pressão superior da cobertura (Ce superior) */
  cpeTop: number;
  /** Coeficiente de pressão interna efetivo sob a cobertura (Cpi inferior) */
  cpiBottom: number;
  /** Coeficiente de força líquida vertical na cobertura (Cnf = cpeTop - cpiBottom) */
  cnfVertical: number;
  /** Coeficiente de força horizontal na parede de fundo */
  cfBack: number;
  /** Coeficiente de força horizontal nas paredes laterais */
  cfSide: number;
  /** Forças totais resultantes (kN) */
  forces: {
    /** Força vertical de arrancamento na cobertura (+ para cima) (kN) */
    fzUp: number;
    /** Força vertical para baixo (kN) */
    fzDown: number;
    /** Força horizontal frontal/empuxo contra a parede de fundo (kN) */
    fxBack: number;
    /** Força horizontal lateral contra paredes (kN) */
    fySide: number;
    /** Força de atrito no plano da cobertura (kN) */
    fFriction: number;
  };
  /** Carga linear estimada nos pórticos metálicos principais (kN/m) (ex: Tubo 150x150) */
  lineLoads: {
    /** Carga horizontal em cada pilar (kN/m) */
    columnLoad: number;
    /** Carga vertical/horizontal na viga em balanço (kN/m) */
    beamLoad: number;
  };
  /** Alívio percentual de arrancamento obtido pela posição da abertura (%) */
  reliefPercentage: number;
  /** Explicação normativa do comportamento */
  statusText: string;
}

export interface ShelterEnvelopeResult {
  res0: ShelterResult;
  res45: ShelterResult;
  res90: ShelterResult;
  criticalUp: { angle: ShelterWindAngle; value: number };
  criticalFx: { angle: ShelterWindAngle; value: number };
  criticalFy: { angle: ShelterWindAngle; value: number };
  criticalColumnLoad: { angle: ShelterWindAngle; value: number };
}

/**
 * Calcula os coeficientes e forças para Abrigos, Marquises e Pórticos
 * considerando incidência do vento a 0° (frontal), 45° (oblíquo) ou 90° (lateral).
 */
export function calculateShelterWind(input: ShelterInput, q: number): ShelterResult {
  const {
    condition,
    depth,
    width,
    height,
    theta,
    backClosure,
    backRange,
    leftClosure,
    rightClosure,
    openingPos,
  } = input;

  const windAngle: ShelterWindAngle = input.windAngle ?? 0;

  const areaRoof = depth * width;
  const areaBack = width * height;
  const areaSide = depth * height;

  // Razões de permeabilidade (0 = aberto, 1 = fechado estanque)
  const bClose = Math.max(0, Math.min(100, backClosure)) / 100;
  const lClose = Math.max(0, Math.min(100, leftClosure)) / 100;
  const rClose = Math.max(0, Math.min(100, rightClosure)) / 100;
  const sideAvgClose = (lClose + rClose) / 2;

  // 1. Coeficiente externo superior na cobertura (Ce superior)
  // Varia conforme o ângulo de incidência (0°, 45° ou 90°)
  let cpeTop = -1.0;
  if (windAngle === 0) {
    // Frontal: -1.0 a -1.4 de sucção de barlavento, dependendo de theta
    cpeTop = theta <= 5 ? -1.0 : theta <= 15 ? -1.2 : -1.4;
  } else if (windAngle === 45) {
    // Oblíquo 45°: forte vórtice cônico de canto nas bordas de barlavento (aumento de sucção em ~20% a 30%)
    cpeTop = theta <= 5 ? -1.3 : theta <= 15 ? -1.4 : -1.6;
  } else {
    // Lateral 90°: escoamento transversal sobre a cobertura, sucção constante por descolamento
    cpeTop = -1.1;
  }

  // 2. Cálculo do Cpi sob a cobertura (efeito estagnação da parede traseira / laterais)
  let baseCpi = 0.0;
  if (windAngle === 0) {
    if (condition === 'cond1') {
      baseCpi = 0.75 * bClose * sideAvgClose;
    } else if (condition === 'cond2') {
      baseCpi = 0.45 * bClose;
    } else if (condition === 'cond3') {
      baseCpi = -0.35 * sideAvgClose;
    } else {
      baseCpi = -0.1 * bClose;
    }
  } else if (windAngle === 45) {
    if (condition === 'cond1') {
      baseCpi = 0.68 * bClose * sideAvgClose;
    } else if (condition === 'cond2') {
      baseCpi = 0.35 * bClose;
    } else if (condition === 'cond3') {
      baseCpi = 0.25 * sideAvgClose;
    } else {
      baseCpi = -0.1 * bClose;
    }
  } else {
    // windAngle === 90
    if (condition === 'cond1') {
      // Estanque 3 lados: vento a 90° incide sobre lateral fechada com fundo fechado retendo o ar
      baseCpi = 0.65 * sideAvgClose * bClose;
    } else if (condition === 'cond2') {
      // Fundo fechado, mas laterais ABERTAS: vento a 90° passa livremente sob o telhado -> sucção
      baseCpi = -0.25;
    } else if (condition === 'cond3') {
      // Túnel (laterais fechadas, fundo aberto): lateral se comporta como parede frontal
      baseCpi = 0.50 * sideAvgClose;
    } else {
      baseCpi = -0.1 * bClose;
    }
  }

  // 3. Efeito da posição da abertura (Topo vs Base vs Distribuída)
  let reliefPercentage = 0;
  let effectiveCpi = baseCpi;

  if (backRange && bClose > 0.05 && bClose < 0.99) {
    const topGapPct = Math.max(0, Math.min(100, 100 - backRange[1])) / 100;
    reliefPercentage = Math.round(35 * Math.min(1, topGapPct * 2.5));
    effectiveCpi = baseCpi * (1 - reliefPercentage / 100);
  } else if (bClose > 0.05 && bClose < 0.99) {
    const openRatio = 1 - bClose;
    if (openingPos === 'top') {
      reliefPercentage = Math.round(35 * openRatio);
      effectiveCpi = baseCpi * (1 - reliefPercentage / 100);
    } else if (openingPos === 'bottom') {
      reliefPercentage = 0;
      effectiveCpi = baseCpi * 1.05;
    } else {
      reliefPercentage = Math.round(18 * openRatio);
      effectiveCpi = baseCpi * (1 - reliefPercentage / 100);
    }
  }

  // Cnf vertical total na cobertura (arrancamento para cima = negativo)
  const cnfUp = cpeTop - Math.max(0, effectiveCpi);
  const cnfDown = 0.2 - Math.min(0, effectiveCpi);

  // 4. Coeficientes horizontais em paredes de fundo e laterais
  let cfBack = 1.3 * bClose;
  let cfSide = 0.9 * sideAvgClose;

  if (windAngle === 45) {
    cfBack = 1.05 * bClose;
    cfSide = 1.05 * sideAvgClose;
  } else if (windAngle === 90) {
    // Vento a 90°: as paredes laterais viram superfície frontal barlavento
    cfSide = 1.3 * sideAvgClose;
    cfBack = 0.7 * bClose;
  }

  // 5. Forças globais (kN)
  const fzUp = Math.abs(cnfUp) * q * areaRoof;
  const fzDown = Math.abs(cnfDown) * q * areaRoof;
  const fxBack = cfBack * q * areaBack;
  const fySide = cfSide * q * (areaSide * 2);
  const fFriction = 0.04 * q * areaRoof;

  // 6. Carga linear em pórticos (2 pórticos principais)
  const numPorticos = 2;
  let columnLoad = 0;
  if (windAngle === 0) {
    columnLoad = (fxBack / height / numPorticos) + (q * 1.8 * 0.15);
  } else if (windAngle === 45) {
    const combForce = Math.sqrt(fxBack * fxBack + fySide * fySide);
    columnLoad = (combForce / height / numPorticos) + (q * 1.8 * 0.15);
  } else {
    // Em 90°, a carga transversal principal atua nas paredes laterais (Fy)
    columnLoad = (fySide / height / numPorticos) + (q * 1.8 * 0.15);
  }

  const beamLoad = (fzUp / depth / numPorticos);

  // Texto explicativo por condição e ângulo
  let statusText = '';
  const anglePrefix =
    windAngle === 0
      ? 'Vento Frontal (0°)'
      : windAngle === 45
      ? 'Vento Oblíquo (45°)'
      : 'Vento Transversal / Lateral (90°)';

  switch (condition) {
    case 'cond1':
      statusText = `${anglePrefix} — Abrigo estanque em 3 lados (Condição 1): Forte retenção de pressão positiva sob a cobertura. ${
        windAngle === 45
          ? 'O vento a 45° gera pico crítico de arrancamento devido a vórtices de canto em barlavento.'
          : windAngle === 90
          ? 'O vento a 90° incide diretamente na parede lateral com acúmulo contra o fundo fechado.'
          : 'Gera pico crítico de arrancamento vertical e empuxo na parede de fundo.'
      }`;
      break;
    case 'cond2':
      statusText = `${anglePrefix} — Abrigo com fundo fechado e laterais abertas (Condição 2): ${
        windAngle === 90
          ? 'Com incidência a 90°, o vento escoa livremente entre as laterais abertas sem gerar sobrepressão (+Cpi) sob o telhado.'
          : 'A parede de fundo atua como anteparo, elevando a sobrepressão sob o balanço.'
      }`;
      break;
    case 'cond3':
      statusText = `${anglePrefix} — Abrigo tipo túnel (Condição 3): ${
        windAngle === 90
          ? 'Com vento a 90°, o fechamento lateral comporta-se como anteparo barlavento, gerando empuxo transversal elevado.'
          : 'Aberturas frontal e traseira provocam efeito Venturi, com sucção interna na cobertura.'
      }`;
      break;
    case 'cond4':
      statusText = `${anglePrefix} — Cobertura livre (Condição 4): Escoamento desimpedido ao redor e sob o telhado, com ações orientadas pela Tabela 24 da NBR 6123.`;
      break;
  }

  return {
    windAngle,
    cpeTop: Number(cpeTop.toFixed(2)),
    cpiBottom: Number(effectiveCpi.toFixed(2)),
    cnfVertical: Number(cnfUp.toFixed(2)),
    cfBack: Number(cfBack.toFixed(2)),
    cfSide: Number(cfSide.toFixed(2)),
    forces: {
      fzUp: Number(fzUp.toFixed(2)),
      fzDown: Number(fzDown.toFixed(2)),
      fxBack: Number(fxBack.toFixed(2)),
      fySide: Number(fySide.toFixed(2)),
      fFriction: Number(fFriction.toFixed(3)),
    },
    lineLoads: {
      columnLoad: Number(columnLoad.toFixed(2)),
      beamLoad: Number(beamLoad.toFixed(2)),
    },
    reliefPercentage,
    statusText,
  };
}

/**
 * Calcula a envoltória e os resultados para as 3 direções de vento (0°, 45° e 90°).
 */
export function calculateShelterWindAllAngles(input: ShelterInput, q: number): ShelterEnvelopeResult {
  const res0 = calculateShelterWind({ ...input, windAngle: 0 }, q);
  const res45 = calculateShelterWind({ ...input, windAngle: 45 }, q);
  const res90 = calculateShelterWind({ ...input, windAngle: 90 }, q);

  const angles: ShelterWindAngle[] = [0, 45, 90];
  const results = [res0, res45, res90];

  let critUpIdx = 0;
  let critFxIdx = 0;
  let critFyIdx = 0;
  let critColIdx = 0;

  for (let i = 1; i < 3; i++) {
    if (results[i].forces.fzUp > results[critUpIdx].forces.fzUp) critUpIdx = i;
    if (results[i].forces.fxBack > results[critFxIdx].forces.fxBack) critFxIdx = i;
    if (results[i].forces.fySide > results[critFyIdx].forces.fySide) critFyIdx = i;
    if (results[i].lineLoads.columnLoad > results[critColIdx].lineLoads.columnLoad) critColIdx = i;
  }

  return {
    res0,
    res45,
    res90,
    criticalUp: { angle: angles[critUpIdx], value: results[critUpIdx].forces.fzUp },
    criticalFx: { angle: angles[critFxIdx], value: results[critFxIdx].forces.fxBack },
    criticalFy: { angle: angles[critFyIdx], value: results[critFyIdx].forces.fySide },
    criticalColumnLoad: { angle: angles[critColIdx], value: results[critColIdx].lineLoads.columnLoad },
  };
}

