import { useState, useEffect, useCallback, useRef } from 'react';
import { HNSLeftPanel } from './HNSLeftPanel';
import type { HNSInputParams } from './HNSLeftPanel';
import { HNSRightPanel } from './HNSRightPanel';
import { MapView } from '@common/components/map/MapView';
import { HNSAnalysisListTable } from './HNSAnalysisListTable';
import { HNSTheoryView } from './HNSTheoryView';
import { HNSSubstanceView } from './HNSSubstanceView';
import { HNSScenarioView } from './HNSScenarioView';
import { HNSRecalcModal } from './HNSRecalcModal';
import type { RecalcParams } from './HNSRecalcModal';
import { useSubMenu, navigateToTab, setReportGenCategory, setHnsReportPayload } from '@common/hooks/useSubMenu';
import { windDirToCompass } from '../hooks/useWeatherFetch';
import { useAuthStore } from '@common/store/authStore';
import { createHnsAnalysis, saveHnsAnalysis, fetchHnsAnalysis } from '../services/hnsApi';
import { computeDispersion } from '../utils/dispersionEngine';
import { getSubstanceToxicity } from '../utils/toxicityData';
import type {
  DispersionPoint, DispersionGridResult, DispersionModel,
  MeteoParams, SourceParams, SimParams, AlgorithmType,
} from '../utils/dispersionTypes';

/* ─── HNS 매뉴얼 뷰어 컴포넌트 ─── */
function HNSManualViewer() {
  const card = 'rounded-md p-4 mb-3'

  return (
    <div className="flex-1 overflow-y-auto bg-bg-0" style={{ scrollbarWidth: 'thin', scrollbarColor: 'var(--bdL) transparent' }}>
      <div className="px-5 py-4 max-w-[1200px] mx-auto">

        {/* 헤더 */}
        <div className="flex items-center justify-between mb-4">
          <div>
            <div className="text-base font-bold">📖 해양 HNS 대응 매뉴얼</div>
            <div className="text-[10px] text-text-3 mt-0.5">Marine HNS Response Manual — Bonn Agreement / HELCOM / REMPEC (WestMOPoCo 2024 한국어판)</div>
          </div>
        </div>

        {/* 목차 카드 그리드 */}
        <div className="grid mb-5" style={{ gridTemplateColumns: 'repeat(4,1fr)', gap: '10px' }}>
          {[
            { icon: '📘', title: '1. 서론', desc: 'HNS 정의 · OPRC-HNS 의정서 · HNS 협약 범위 및 목적', color: 'var(--cyan)' },
            { icon: '⚖️', title: '2. IMO 협약·의정서·규칙', desc: 'SOLAS · MARPOL · IBC Code · IMDG Code · IGC Code', color: 'var(--cyan)' },
            { icon: '🔬', title: '3. HNS 거동 및 유해요소', desc: 'SEBC 거동분류 · MSDS · GESAMP · 물리화학적 특성', color: 'var(--purple)' },
            { icon: '🛡️', title: '4. 대비', desc: '위험 평가 · 비상 계획 · 교육훈련 · 장비 비축', color: 'var(--orange)' },
            { icon: '🚨', title: '5. 대응', desc: '최초 조치 · 안전구역 · PPE · 모니터링 · 대응 기술', color: 'var(--red)' },
            { icon: '🔄', title: '6. 유출 후 관리', desc: '비용 문서화 · 환경 회복 · 사고 검토 · 교훈', color: 'var(--cyan)' },
            { icon: '📋', title: '7. 사례연구', desc: '실제 HNS 해양사고 사례 분석 및 교훈', color: 'var(--cyan)' },
            { icon: '📊', title: '8. 자료표', desc: '물질별 데이터시트 · AEGL · 노출 한계값', color: 'var(--cyan)' },
          ].map(ch => (
            <div key={ch.title} className={card} style={{ background: 'var(--bg3)', border: '1px solid var(--bd)', cursor: 'pointer', transition: '.2s' }}>
              <div className="text-[20px] mb-1.5">{ch.icon}</div>
              <div className="text-[11px] font-bold">{ch.title}</div>
              <div className="text-[9px] text-text-3 mt-1 leading-[1.4]">{ch.desc}</div>
            </div>
          ))}
        </div>

        {/* SEBC 거동 분류 */}
        <div className={`${card} bg-bg-3 border border-border`}>
          <div className="text-[13px] font-bold mb-2.5">SEBC 거동 분류 (Standard European Behaviour Classification)</div>
          <div className="text-[9px] text-text-3 mb-2.5 leading-normal">물질의 물리적·화학적 특성(용해도, 밀도, 증기압, 점도)에 따라 이론적 거동을 5가지 주요 범주 + 7가지 하위 범주로 분류</div>
          <div className="grid gap-2" style={{ gridTemplateColumns: 'repeat(5,1fr)' }}>
            {[
              { icon: '💨', label: 'G — 가스', desc: '대기 중 확산\n증기압 > 101.3kPa\n예: 암모니아, 염소', color: 'rgba(139,92,246' },
              { icon: '🌫️', label: 'E — 증발', desc: '수면→대기 증발\n증기압 > 3kPa\n예: 벤젠, 톨루엔', color: 'rgba(249,115,22' },
              { icon: '🟡', label: 'F — 부유', desc: '해수면에 부유\n밀도 < 1.025\n예: 스티렌, 크실렌', color: 'rgba(251,191,36' },
              { icon: '💧', label: 'D — 용해', desc: '해수에 용해\n용해도 > 5%\n예: 메탄올, 염산', color: 'rgba(6,182,212' },
              { icon: '⬇️', label: 'S — 침강', desc: '해저로 침강\n밀도 > 1.025\n예: EDC, 사염화탄소', color: 'rgba(139,148,158' },
            ].map(s => (
              <div key={s.label} className="text-center px-[6px] py-[10px] rounded-sm" style={{ background: `${s.color},.08)`, border: `1px solid ${s.color},.2)` }}>
                <div className="text-[22px] mb-1">{s.icon}</div>
                <div className="text-[11px] font-bold" style={{ color: `${s.color},1)` }}>{s.label}</div>
                <div className="text-text-3 whitespace-pre-line text-[8px] mt-[3px] leading-[1.3]">{s.desc}</div>
              </div>
            ))}
          </div>
        </div>

        {/* 출처 */}
        <div className="text-text-3 rounded-sm bg-bg-3 p-[10px] text-[8px] leading-[1.5]">
          <b>출처:</b> Marine HNS Response Manual — Bonn Agreement / HELCOM / REMPEC (WestMOPoCo Project, 2024 한국어판)<br />
          번역: 원해민, 이시연, 양보경, 강성길, 이성엽 — KRISO 선박해양플랜트연구소 / NOWPAP MERRAC<br />
          원본: Alcaro L., Brandt J., Giraud W., Mannozzi M., Nicolas-Kopec A. (2021) ISBN: 978-2-87893-147-1
        </div>
      </div>
    </div>
  )
}

/* ─── 시간 슬라이더 컴포넌트 ─── */
function DispersionTimeSlider({
  currentFrame,
  totalFrames,
  isPlaying,
  onFrameChange,
  onPlayPause,
  dt,
}: {
  currentFrame: number;
  totalFrames: number;
  isPlaying: boolean;
  onFrameChange: (frame: number) => void;
  onPlayPause: () => void;
  dt: number;
}) {
  const currentTime = (currentFrame + 1) * dt;
  const endTime = totalFrames * dt;

  return (
    <div
      className="absolute bottom-4 left-1/2 -translate-x-1/2 z-10 flex items-center gap-3 px-4 py-2.5 rounded-lg"
      style={{
        background: 'rgba(10,22,40,0.92)',
        border: '1px solid rgba(6,182,212,0.25)',
        backdropFilter: 'blur(8px)',
        minWidth: '360px',
      }}
    >
      <button
        onClick={onPlayPause}
        className="flex items-center justify-center w-7 h-7 rounded-full text-[14px]"
        style={{
          background: isPlaying ? 'rgba(239,68,68,0.2)' : 'rgba(6,182,212,0.2)',
          border: `1px solid ${isPlaying ? 'rgba(239,68,68,0.4)' : 'rgba(6,182,212,0.4)'}`,
          cursor: 'pointer',
        }}
      >
        {isPlaying ? '⏸' : '▶'}
      </button>

      <div className="flex-1 flex flex-col gap-1">
        <div className="flex items-center justify-between text-[9px]">
          <span className="text-primary-cyan font-mono font-bold">t = {currentTime}s</span>
          <span className="text-text-3 font-mono">{endTime}s</span>
        </div>
        <input
          type="range"
          min={0}
          max={totalFrames - 1}
          value={currentFrame}
          onChange={(e) => onFrameChange(parseInt(e.target.value))}
          className="w-full h-1 accent-[var(--cyan)]"
          style={{ cursor: 'pointer' }}
        />
      </div>

      <div className="text-[9px] text-text-3 font-mono whitespace-nowrap">
        {currentFrame + 1}/{totalFrames}
      </div>
    </div>
  );
}

/* ─── 메인 HNSView ─── */
export function HNSView() {
  const { activeSubTab, setActiveSubTab } = useSubMenu('hns');
  const { user } = useAuthStore();
  const [incidentCoord, setIncidentCoord] = useState({ lon: 129.3542, lat: 35.4215 });
  const [isSelectingLocation, setIsSelectingLocation] = useState(false);
  const [isRunningPrediction, setIsRunningPrediction] = useState(false);
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const [dispersionResult, setDispersionResult] = useState<any>(null);
  const [recalcModalOpen, setRecalcModalOpen] = useState(false);

  // 확산 엔진 결과
  const [heatmapData, setHeatmapData] = useState<DispersionPoint[]>([]);
  const [computedResult, setComputedResult] = useState<DispersionGridResult | null>(null);
  const [allTimeFrames, setAllTimeFrames] = useState<DispersionGridResult[]>([]);
  const [currentFrame, setCurrentFrame] = useState(0);
  const [isPuffPlaying, setIsPuffPlaying] = useState(false);

  // 좌측 패널 입력 파라미터 (state로 관리하여 변경 시 재계산 트리거)
  const [inputParams, setInputParams] = useState<HNSInputParams | null>(null);
  const [loadedParams, setLoadedParams] = useState<Partial<HNSInputParams> | null>(null);
  const hasRunOnce = useRef(false); // 최초 실행 여부
  const mapCaptureRef = useRef<(() => string | null) | null>(null);

  const handleReset = useCallback(() => {
    setDispersionResult(null);
    setHeatmapData([]);
    setComputedResult(null);
    setAllTimeFrames([]);
    setCurrentFrame(0);
    setIsPuffPlaying(false);
    setInputParams(null);
    hasRunOnce.current = false;
  }, []);

  const handleParamsChange = useCallback((params: HNSInputParams) => {
    setInputParams(params);
  }, []);

  const handleMapClick = (lon: number, lat: number) => {
    if (isSelectingLocation) {
      setIncidentCoord({ lon, lat });
      setIsSelectingLocation(false);
    }
  };

  // 시간 애니메이션 (puff/dense_gas)
  useEffect(() => {
    if (!isPuffPlaying || allTimeFrames.length === 0) return;

    const interval = setInterval(() => {
      setCurrentFrame(prev => {
        const next = prev + 1;
        if (next >= allTimeFrames.length) {
          setIsPuffPlaying(false);
          return prev;
        }
        setHeatmapData(allTimeFrames[next].points);
        setComputedResult(allTimeFrames[next]);
        return next;
      });
    }, 300);

    return () => clearInterval(interval);
  }, [isPuffPlaying, allTimeFrames]);

  const handleFrameChange = (frame: number) => {
    setCurrentFrame(frame);
    if (allTimeFrames[frame]) {
      setHeatmapData(allTimeFrames[frame].points);
      setComputedResult(allTimeFrames[frame]);
    }
  };

  /** 확산 계산 핵심 로직 (버튼 클릭 + 파라미터 변경 시 공유) */
  const runComputation = useCallback((params: HNSInputParams | null, coord: { lon: number; lat: number }) => {
    const substanceName = params?.substance || '톨루엔 (Toluene)';
    const tox = getSubstanceToxicity(substanceName);

    const weather = params?.weather;
    const meteo: MeteoParams = {
      windSpeed: weather?.windSpeed ?? 5.0,
      windDirDeg: weather?.windDirection ?? 270,
      stability: weather?.stability ?? 'D',
      temperature: ((weather?.temperature ?? 15) + 273.15),
      pressure: 101325,
      mixingHeight: 800,
    };

    const releaseType = params?.releaseType || '연속 유출';
    const source: SourceParams = {
      Q: params?.emissionRate ?? tox.Q,
      QTotal: params?.totalRelease ?? tox.QTotal,
      x0: 0, y0: 0,
      z0: params?.releaseHeight ?? 0.5,
      releaseDuration: releaseType === '연속 유출' ? (params?.releaseDuration ?? 300) : 0,
      molecularWeight: tox.mw,
      vaporPressure: tox.vaporPressure,
      densityGas: tox.densityGas,
      poolRadius: params?.poolRadius ?? tox.poolRadius,
    };

    const sim: SimParams = {
      xRange: [-100, 10000],
      yRange: [-2000, 2000],
      nx: 300,
      ny: 200,
      zRef: 1.5,
      tStart: 0,
      tEnd: 600,
      dt: 30,
    };

    const modelType: DispersionModel =
      releaseType === '연속 유출' ? 'plume' :
      releaseType === '순간 유출' ? 'puff' : 'dense_gas';

    const algo = (params?.algorithm || 'ALOHA (EPA)') as AlgorithmType;

    if (modelType === 'plume') {
      const result = computeDispersion({
        meteo, source, sim, modelType,
        originLon: coord.lon, originLat: coord.lat,
        substanceName, t: sim.dt, algorithm: algo,
      });
      console.log('[HNS] plume 계산 완료:', result.points.length, '포인트, maxConc:', result.maxConcentration.toFixed(2), 'ppm');
      setComputedResult(result);
      setHeatmapData(result.points);
      setAllTimeFrames([result]);
      setCurrentFrame(0);
      setIsPuffPlaying(false);
    } else {
      const times: number[] = [];
      for (let t = sim.dt; t <= sim.tEnd; t += sim.dt) times.push(t);
      const frames = times.map(t => computeDispersion({
        meteo, source, sim, modelType,
        originLon: coord.lon, originLat: coord.lat,
        substanceName, t, algorithm: algo,
      }));
      console.log('[HNS]', modelType, '계산 완료:', frames.length, '프레임, 첫 프레임:', frames[0].points.length, '포인트');
      setAllTimeFrames(frames);
      setComputedResult(frames[0]);
      setHeatmapData(frames[0].points);
      setCurrentFrame(0);
      setIsPuffPlaying(true);
    }

    // AEGL 구역 (MapView 레거시 호환)
    const resultForZones = modelType === 'plume'
      ? computeDispersion({
          meteo, source, sim, modelType,
          originLon: coord.lon, originLat: coord.lat,
          substanceName, t: sim.dt, algorithm: algo,
        })
      : computeDispersion({
          meteo, source, sim, modelType,
          originLon: coord.lon, originLat: coord.lat,
          substanceName, t: sim.dt * 5, algorithm: algo,
        });

    return { tox, meteo, resultForZones, substanceName };
  }, []);

  const handleRunPrediction = async (paramsOverride?: HNSInputParams | null) => {
    setIsRunningPrediction(true);

    try {
      const params = paramsOverride ?? inputParams;

      // 1. 계산 먼저 실행 (동기, 히트맵 즉시 표시)
      const { tox, meteo, resultForZones, substanceName } = runComputation(params, incidentCoord);
      hasRunOnce.current = true;

      setDispersionResult({
        hnsAnlysSn: 0,
        zones: [
          { level: 'AEGL-3', color: '#ef4444', radius: resultForZones.aeglDistances.aegl3, angle: meteo.windDirDeg },
          { level: 'AEGL-2', color: '#f97316', radius: resultForZones.aeglDistances.aegl2, angle: meteo.windDirDeg },
          { level: 'AEGL-1', color: '#eab308', radius: resultForZones.aeglDistances.aegl1, angle: meteo.windDirDeg },
        ].filter(z => z.radius > 0),
        timestamp: new Date().toISOString(),
        windDirection: meteo.windDirDeg,
        substance: substanceName,
        concentration: {
          'AEGL-3': `${tox.aegl3} ppm`,
          'AEGL-2': `${tox.aegl2} ppm`,
          'AEGL-1': `${tox.aegl1} ppm`,
        },
        maxConcentration: resultForZones.maxConcentration,
      });

      // 2. 분석 레코드 DB 저장 (비동기, 실패해도 무시)
      try {
        const acdntDtm = params?.accidentDate && params?.accidentTime
          ? `${params.accidentDate}T${params.accidentTime}:00`
          : params?.accidentDate || undefined;
        const result = await createHnsAnalysis({
          anlysNm: params?.accidentName || `HNS 분석 ${new Date().toLocaleDateString('ko-KR')}`,
          acdntDtm,
          lon: incidentCoord.lon,
          lat: incidentCoord.lat,
          locNm: `${incidentCoord.lat.toFixed(4)} / ${incidentCoord.lon.toFixed(4)}`,
          sbstNm: params?.substance,
          windSpd: params?.weather?.windSpeed,
          windDir: params?.weather?.windDirection != null ? String(params.weather.windDirection) : undefined,
          temp: params?.weather?.temperature,
          humid: params?.weather?.humidity,
          atmStblCd: params?.weather?.stability,
          analystNm: user?.name || undefined,
        });
        // DB 저장 성공 시 SN 업데이트
        setDispersionResult((prev: Record<string, unknown> | null) => prev ? { ...prev, hnsAnlysSn: result.hnsAnlysSn } : prev);
      } catch {
        // API 실패 시 무시 (히트맵은 이미 표시됨)
      }

    } catch (error) {
      console.error('대기확산 예측 오류:', error);
      alert('대기확산 예측 중 오류가 발생했습니다.');
    } finally {
      setIsRunningPrediction(false);
    }
  };

  /** 분석 결과 저장 */
  const handleSave = async () => {
    if (!dispersionResult || !inputParams || !computedResult) {
      alert('저장할 분석 결과가 없습니다. 먼저 예측을 실행해주세요.');
      return;
    }

    const rsltData: Record<string, unknown> = {
      inputParams: {
        substance: inputParams.substance,
        releaseType: inputParams.releaseType,
        emissionRate: inputParams.emissionRate,
        totalRelease: inputParams.totalRelease,
        releaseHeight: inputParams.releaseHeight,
        releaseDuration: inputParams.releaseDuration,
        poolRadius: inputParams.poolRadius,
        algorithm: inputParams.algorithm,
        criteriaModel: inputParams.criteriaModel,
        accidentDate: inputParams.accidentDate,
        accidentTime: inputParams.accidentTime,
        predictionTime: inputParams.predictionTime,
        accidentName: inputParams.accidentName,
      },
      coord: { lon: incidentCoord.lon, lat: incidentCoord.lat },
      zones: dispersionResult.zones,
      aeglDistances: computedResult.aeglDistances,
      aeglAreas: computedResult.aeglAreas,
      maxConcentration: computedResult.maxConcentration,
      modelType: computedResult.modelType,
      weather: {
        windSpeed: inputParams.weather.windSpeed,
        windDirection: inputParams.weather.windDirection,
        temperature: inputParams.weather.temperature,
        humidity: inputParams.weather.humidity,
        stability: inputParams.weather.stability,
      },
      aegl3: (computedResult.aeglDistances?.aegl3 ?? 0) > 0,
      aegl2: (computedResult.aeglDistances?.aegl2 ?? 0) > 0,
      aegl1: (computedResult.aeglDistances?.aegl1 ?? 0) > 0,
      damageRadius: `${((computedResult.aeglDistances?.aegl1 ?? 0) / 1000).toFixed(1)} km`,
    };

    // 위험등급 자동 판정
    let riskCd = 'LOW';
    if ((computedResult.aeglDistances?.aegl3 ?? 0) > 0) riskCd = 'CRITICAL';
    else if ((computedResult.aeglDistances?.aegl2 ?? 0) > 0) riskCd = 'HIGH';
    else if ((computedResult.aeglDistances?.aegl1 ?? 0) > 0) riskCd = 'MEDIUM';

    // DB 저장 시도
    let savedToDb = false;
    try {
      let sn = dispersionResult.hnsAnlysSn as number;
      if (!sn) {
        const fcstHrNum = parseInt(inputParams.predictionTime) || 24;
        const spilQtyVal = inputParams.releaseType === '순간 유출'
          ? inputParams.totalRelease
          : inputParams.emissionRate;
        const anlysNm = inputParams.accidentName || `HNS 분석 ${new Date().toLocaleDateString('ko-KR')}`;
        const saveAcdntDtm = inputParams.accidentDate && inputParams.accidentTime
          ? `${inputParams.accidentDate}T${inputParams.accidentTime}:00`
          : inputParams.accidentDate || undefined;
        const created = await createHnsAnalysis({
          anlysNm,
          acdntDtm: saveAcdntDtm,
          lon: incidentCoord.lon,
          lat: incidentCoord.lat,
          locNm: `${incidentCoord.lat.toFixed(4)} / ${incidentCoord.lon.toFixed(4)}`,
          sbstNm: inputParams.substance,
          spilQty: spilQtyVal,
          spilUnitCd: inputParams.releaseType === '순간 유출' ? 'g' : 'g/s',
          fcstHr: fcstHrNum,
          windSpd: inputParams.weather.windSpeed,
          windDir: String(inputParams.weather.windDirection),
          algoCd: inputParams.algorithm,
          critMdlCd: inputParams.criteriaModel,
          analystNm: user?.name || undefined,
        });
        sn = created.hnsAnlysSn;
        setDispersionResult((prev: Record<string, unknown> | null) => prev ? { ...prev, hnsAnlysSn: sn } : prev);
      }
      await saveHnsAnalysis(sn, { rsltData, execSttsCd: 'COMPLETED', riskCd });
      savedToDb = true;
    } catch {
      // DB 저장 실패 — localStorage fallback
    }

    // localStorage에도 저장 (DB 실패 시 fallback, 성공 시 백업)
    try {
      const localKey = 'hns_saved_analyses';
      const existing: Record<string, unknown>[] = JSON.parse(localStorage.getItem(localKey) || '[]');
      const fcstHrLocal = parseInt(inputParams.predictionTime) || 24;
      const spilQtyLocal = inputParams.releaseType === '순간 유출'
        ? inputParams.totalRelease
        : inputParams.emissionRate;
      const entry = {
        id: Date.now(),
        anlysNm: inputParams.accidentName || `HNS 분석 ${new Date().toLocaleDateString('ko-KR')}`,
        acdntDtm: inputParams.accidentDate && inputParams.accidentTime
          ? `${inputParams.accidentDate}T${inputParams.accidentTime}:00`
          : inputParams.accidentDate || null,
        sbstNm: inputParams.substance,
        analystNm: user?.name || null,
        spilQty: spilQtyLocal,
        spilUnitCd: inputParams.releaseType === '순간 유출' ? 'g' : 'g/s',
        fcstHr: fcstHrLocal,
        regDtm: new Date().toISOString(),
        riskCd,
        rsltData,
      };
      existing.unshift(entry);
      // 최대 50건 유지
      if (existing.length > 50) existing.length = 50;
      localStorage.setItem(localKey, JSON.stringify(existing));
    } catch {
      // localStorage 저장 실패 무시
    }

    alert(savedToDb ? '분석 결과가 저장되었습니다.' : '분석 결과가 로컬에 저장되었습니다. (서버 연결 실패)');
  };

  /** 분석 목록에서 불러오기 (DB 또는 localStorage) */
  const handleLoadAnalysis = async (sn: number, localRsltData?: Record<string, unknown>) => {
    try {
      let rslt: Record<string, unknown>;

      if (localRsltData) {
        // localStorage에서 직접 전달된 데이터
        rslt = localRsltData;
      } else {
        // DB에서 조회
        const analysis = await fetchHnsAnalysis(sn);
        if (!analysis.rsltData) {
          alert('저장된 분석 결과가 없습니다.');
          return;
        }
        rslt = analysis.rsltData as Record<string, unknown>;
      }
      const savedParams = rslt.inputParams as Record<string, unknown> | undefined;
      const savedCoord = rslt.coord as { lon: number; lat: number } | undefined;

      // 좌표 복원
      if (savedCoord) {
        setIncidentCoord(savedCoord);
      }

      // 입력 파라미터 복원 → HNSLeftPanel에 전달
      if (savedParams) {
        setLoadedParams({
          substance: savedParams.substance as string,
          releaseType: savedParams.releaseType as HNSInputParams['releaseType'],
          emissionRate: savedParams.emissionRate as number,
          totalRelease: savedParams.totalRelease as number,
          releaseHeight: savedParams.releaseHeight as number,
          releaseDuration: savedParams.releaseDuration as number,
          poolRadius: savedParams.poolRadius as number,
          algorithm: savedParams.algorithm as string,
          criteriaModel: savedParams.criteriaModel as string,
          accidentDate: savedParams.accidentDate as string,
          accidentTime: savedParams.accidentTime as string,
          predictionTime: savedParams.predictionTime as string,
          accidentName: savedParams.accidentName as string,
        });
      }

      // 탭 전환 → analysis
      setActiveSubTab('analysis');

      // 약간의 딜레이 후 계산 실행 (loadedParams → inputParams 동기화 대기)
      setTimeout(() => {
        if (savedParams && savedCoord) {
          const savedWeather = rslt.weather as Record<string, unknown> | undefined;
          const params: HNSInputParams = {
            substance: (savedParams.substance as string) || '톨루엔 (Toluene)',
            releaseType: (savedParams.releaseType as HNSInputParams['releaseType']) || '연속 유출',
            emissionRate: (savedParams.emissionRate as number) || 10,
            totalRelease: (savedParams.totalRelease as number) || 5000,
            releaseHeight: (savedParams.releaseHeight as number) || 0.5,
            releaseDuration: (savedParams.releaseDuration as number) || 300,
            poolRadius: (savedParams.poolRadius as number) || 5,
            algorithm: (savedParams.algorithm as string) || 'ALOHA (EPA)',
            criteriaModel: (savedParams.criteriaModel as string) || 'AEGL',
            accidentDate: (savedParams.accidentDate as string) || '',
            accidentTime: (savedParams.accidentTime as string) || '',
            predictionTime: (savedParams.predictionTime as string) || '24시간',
            accidentName: (savedParams.accidentName as string) || '',
            weather: {
              windSpeed: (savedWeather?.windSpeed as number) ?? 5.0,
              windDirection: (savedWeather?.windDirection as number) ?? 270,
              temperature: (savedWeather?.temperature as number) ?? 15,
              humidity: (savedWeather?.humidity as number) ?? 60,
              stability: (savedWeather?.stability as string ?? 'D') as HNSInputParams['weather']['stability'],
              isLoading: false,
              error: null,
              lastUpdate: null,
            },
          };

          setInputParams(params);

          try {
            const { tox, meteo, resultForZones, substanceName } = runComputation(params, savedCoord);
            hasRunOnce.current = true;

            setDispersionResult({
              hnsAnlysSn: sn,
              zones: [
                { level: 'AEGL-3', color: 'rgba(239,68,68,0.4)', radius: resultForZones.aeglDistances.aegl3, angle: meteo.windDirDeg },
                { level: 'AEGL-2', color: 'rgba(249,115,22,0.3)', radius: resultForZones.aeglDistances.aegl2, angle: meteo.windDirDeg },
                { level: 'AEGL-1', color: 'rgba(234,179,8,0.25)', radius: resultForZones.aeglDistances.aegl1, angle: meteo.windDirDeg },
              ].filter((z: { radius: number }) => z.radius > 0),
              timestamp: new Date().toISOString(),
              windDirection: meteo.windDirDeg,
              substance: substanceName,
              concentration: {
                'AEGL-3': `${tox.aegl3} ppm`,
                'AEGL-2': `${tox.aegl2} ppm`,
                'AEGL-1': `${tox.aegl1} ppm`,
              },
              maxConcentration: resultForZones.maxConcentration,
            });
          } catch {
            // 재계산 실패 시 무시
          }
        }
      }, 200);
    } catch {
      alert('분석 불러오기에 실패했습니다.');
    }
  };

  /** 보고서 생성 — 실 데이터 수집 + 지도 캡처 후 탭 이동 */
  const handleOpenReport = () => {
    try {
      let mapImage: string | null = null;
      try { mapImage = mapCaptureRef.current?.() ?? null; } catch { /* canvas capture 실패 무시 */ }

      const tox = getSubstanceToxicity(inputParams?.substance || '');
      const modelType = computedResult?.modelType;
      const modelLabel = modelType === 'plume' ? 'Gaussian Plume'
        : modelType === 'puff' ? 'Gaussian Puff'
        : modelType === 'dense_gas' ? 'Dense Gas (B-M)' : 'ALOHA';

      setHnsReportPayload({
        mapImageDataUrl: mapImage,
        substance: {
          name: inputParams?.substance || '—',
          toxicity: `AEGL-2: ${tox.aegl2} ppm / AEGL-3: ${tox.aegl3} ppm`,
        },
        hazard: {
          aegl3: `${((computedResult?.aeglDistances.aegl3 ?? 0) / 1000).toFixed(1)} km`,
          aegl2: `${((computedResult?.aeglDistances.aegl2 ?? 0) / 1000).toFixed(1)} km`,
          aegl1: `${((computedResult?.aeglDistances.aegl1 ?? 0) / 1000).toFixed(1)} km`,
        },
        atm: {
          model: modelLabel,
          maxDistance: `${((computedResult?.aeglDistances.aegl1 ?? 0) / 1000).toFixed(1)} km`,
        },
        weather: {
          windDir: `${windDirToCompass(inputParams?.weather?.windDirection ?? 0)} ${inputParams?.weather?.windDirection ?? 0}°`,
          windSpeed: `${inputParams?.weather?.windSpeed ?? 0} m/s`,
          stability: `${inputParams?.weather?.stability ?? 'D'}`,
          temperature: `${inputParams?.weather?.temperature ?? 0}°C`,
        },
        maxConcentration: `${(computedResult?.maxConcentration ?? 0).toFixed(1)} ppm`,
        aeglAreas: {
          aegl1: `${(computedResult?.aeglAreas.aegl1 ?? 0).toFixed(2)} km²`,
          aegl2: `${(computedResult?.aeglAreas.aegl2 ?? 0).toFixed(2)} km²`,
          aegl3: `${(computedResult?.aeglAreas.aegl3 ?? 0).toFixed(2)} km²`,
        },
      });
    } catch (err) {
      console.error('[HNS] 보고서 데이터 수집 오류:', err);
    }
    setReportGenCategory(1);
    navigateToTab('reports', 'generate');
  };

  if (activeSubTab === 'scenario') {
    return <HNSScenarioView />;
  }

  if (activeSubTab === 'manual') {
    return <HNSManualViewer />;
  }

  if (activeSubTab === 'theory') {
    return <HNSTheoryView />;
  }

  if (activeSubTab === 'substance') {
    return <HNSSubstanceView />;
  }

  return (
    <div className="flex flex-1 overflow-hidden">
        {/* Left Panel - 분석 목록일 때는 숨김 */}
        {activeSubTab === 'analysis' && (
          <HNSLeftPanel
            activeSubTab={activeSubTab}
            onSubTabChange={setActiveSubTab}
            incidentCoord={incidentCoord}
            onCoordChange={setIncidentCoord}
            onMapSelectClick={() => setIsSelectingLocation(true)}
            onRunPrediction={handleRunPrediction}
            isRunningPrediction={isRunningPrediction}
            onParamsChange={handleParamsChange}
            onReset={handleReset}
            loadedParams={loadedParams}
          />
        )}

        {/* Center - Map/Content Area */}
        <div className="flex-1 relative overflow-hidden">
          {activeSubTab === 'list' ? (
            <HNSAnalysisListTable onTabChange={(v) => setActiveSubTab(typeof v === 'function' ? v(activeSubTab as 'analysis' | 'list') : v)} onSelectAnalysis={handleLoadAnalysis} />
          ) : (
            <>
              <MapView
                incidentCoord={incidentCoord}
                isSelectingLocation={isSelectingLocation}
                onMapClick={handleMapClick}
                oilTrajectory={[]}
                enabledLayers={new Set()}
                dispersionResult={dispersionResult}
                dispersionHeatmap={heatmapData}
                mapCaptureRef={mapCaptureRef}
              />
              {/* 시간 슬라이더 (puff/dense_gas 모델용) */}
              {allTimeFrames.length > 1 && (
                <DispersionTimeSlider
                  currentFrame={currentFrame}
                  totalFrames={allTimeFrames.length}
                  isPlaying={isPuffPlaying}
                  onFrameChange={handleFrameChange}
                  onPlayPause={() => setIsPuffPlaying(!isPuffPlaying)}
                  dt={30}
                />
              )}
            </>
          )}
        </div>

      {/* Right Panel - 분석 목록일 때는 숨김 */}
      {activeSubTab === 'analysis' && (
        <HNSRightPanel
          dispersionResult={dispersionResult}
          computedResult={computedResult}
          weatherData={inputParams?.weather ?? null}
          onOpenRecalc={() => setRecalcModalOpen(true)}
          onOpenReport={handleOpenReport}
          onSave={handleSave}
        />
      )}

      {/* HNS 재계산 모달 */}
      <HNSRecalcModal
        isOpen={recalcModalOpen}
        onClose={() => setRecalcModalOpen(false)}
        currentParams={inputParams ? {
          substance: inputParams.substance,
          releaseType: inputParams.releaseType,
          emissionRate: inputParams.emissionRate,
          totalRelease: inputParams.totalRelease,
          algorithm: inputParams.algorithm,
          predictionTime: inputParams.predictionTime,
        } : null}
        onSubmit={(recalcP: RecalcParams) => {
          // 재계산 파라미터를 현재 inputParams에 병합하여 즉시 실행
          const merged: HNSInputParams = {
            ...(inputParams as HNSInputParams),
            substance: recalcP.substance,
            releaseType: recalcP.releaseType,
            emissionRate: recalcP.emissionRate,
            totalRelease: recalcP.totalRelease,
            algorithm: recalcP.algorithm,
            predictionTime: recalcP.predictionTime,
          };
          // 좌측 패널 UI도 동기화
          setLoadedParams(merged);
          setInputParams(merged);
          // 병합된 파라미터로 즉시 예측 실행
          handleRunPrediction(merged);
        }}
      />
    </div>
  );
}
