import { useMemo, useRef } from 'react'; import type { Ship } from '../../types'; import { getMarineTrafficCategory } from '../../utils/marineTraffic'; import { aggregateFishingStats, GEAR_LABELS, classifyFishingZone } from '../../utils/fishingAnalysis'; import type { FishingGearType } from '../../utils/fishingAnalysis'; interface Props { ships: Ship[]; onClose: () => void; } function now() { const d = new Date(); return `${d.getFullYear()}.${String(d.getMonth() + 1).padStart(2, '0')}.${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`; } export function ReportModal({ ships, onClose }: Props) { const reportRef = useRef(null); const timestamp = useMemo(() => now(), []); // Ship statistics const stats = useMemo(() => { const kr = ships.filter(s => s.flag === 'KR'); const cn = ships.filter(s => s.flag === 'CN'); const cnFishing = cn.filter(s => { const cat = getMarineTrafficCategory(s.typecode, s.category); return cat === 'fishing' || s.category === 'fishing' || s.typecode === '30'; }); // CN fishing by speed const cnAnchored = cnFishing.filter(s => s.speed < 1); const cnLowSpeed = cnFishing.filter(s => s.speed >= 1 && s.speed < 3); const cnOperating = cnFishing.filter(s => s.speed >= 2 && s.speed <= 6); const cnSailing = cnFishing.filter(s => s.speed > 6); // Gear analysis const fishingStats = aggregateFishingStats(cn); // Zone analysis const zoneStats: Record = { ZONE_I: 0, ZONE_II: 0, ZONE_III: 0, ZONE_IV: 0, OUTSIDE: 0 }; cnFishing.forEach(s => { const z = classifyFishingZone(s.lat, s.lng); zoneStats[z.zone] = (zoneStats[z.zone] || 0) + 1; }); // Dark vessels (AIS gap) const darkSuspect = cnFishing.filter(s => s.speed === 0 && (!s.heading || s.heading === 0)); // Ship types const byType: Record = {}; ships.forEach(s => { const cat = getMarineTrafficCategory(s.typecode, s.category); byType[cat] = (byType[cat] || 0) + 1; }); // By nationality top 10 const byFlag: Record = {}; ships.forEach(s => { byFlag[s.flag || 'unknown'] = (byFlag[s.flag || 'unknown'] || 0) + 1; }); const topFlags = Object.entries(byFlag).sort((a, b) => b[1] - a[1]).slice(0, 10); return { total: ships.length, kr, cn, cnFishing, cnAnchored, cnLowSpeed, cnOperating, cnSailing, fishingStats, zoneStats, darkSuspect, byType, topFlags }; }, [ships]); const handlePrint = () => { const content = reportRef.current; if (!content) return; const win = window.open('', '_blank'); if (!win) return; win.document.write(` 중국어선 감시현황 보고서 - ${timestamp} ${content.innerHTML} `); win.document.close(); win.print(); }; const gearEntries = Object.entries(stats.fishingStats.byGear) as [FishingGearType, number][]; return (
e.stopPropagation()} > {/* Toolbar */}
📋 중국어선 감시현황 분석 보고서 {timestamp} 기준
{/* Report Content */}

한중어업협정 기반 중국어선 감시 현황 분석 보고서

문서번호: GC-KCG-RPT-AUTO | 생성일시: {timestamp} | 작성: KCG AI 자동분석 시스템 | 【대외비】
{/* 1. 전체 현황 */}

1. 전체 해양 현황

구분척수비율
전체 선박{stats.total.toLocaleString()}척100%
🇰🇷 한국 선박{stats.kr.length.toLocaleString()}척{pct(stats.kr.length, stats.total)}
🇨🇳 중국 선박{stats.cn.length.toLocaleString()}척{pct(stats.cn.length, stats.total)}
🇨🇳 중국어선{stats.cnFishing.length.toLocaleString()}척{pct(stats.cnFishing.length, stats.total)}
{/* 2. 중국어선 상세 */}

2. 중국어선 활동 분석

활동 상태척수비율판단 기준
⚓ 정박 (0~1kn){stats.cnAnchored.length}{pct(stats.cnAnchored.length, stats.cnFishing.length)}SOG {'<'} 1 knot
🔵 저속 이동 (1~3kn){stats.cnLowSpeed.length}{pct(stats.cnLowSpeed.length, stats.cnFishing.length)}투·양망 또는 이동
🟡 조업 추정 (2~6kn){stats.cnOperating.length}{pct(stats.cnOperating.length, stats.cnFishing.length)}트롤/자망 조업 속도
🟢 항해 중 (6+kn){stats.cnSailing.length}{pct(stats.cnSailing.length, stats.cnFishing.length)}이동/귀항
{/* 3. 어구별 분석 */}

3. 어구/어망 유형별 분석

{gearEntries.map(([gear, count]) => { const meta = GEAR_LABELS[gear]; return ( ); })}
어구 유형추정 척수위험도탐지 신뢰도
{meta?.icon || '🎣'} {meta?.label || gear} {count}척 {meta?.riskLevel === 'CRITICAL' ? '◉ CRITICAL' : meta?.riskLevel === 'HIGH' ? '⚠ HIGH' : '△ MED'} {meta?.confidence || '-'}
{/* 4. 수역별 분포 */}

4. 특정어업수역별 분포

수역어선 수허가 업종 (3월)비고
수역 I (동해){stats.zoneStats.ZONE_I}PS, FC만PT/OT/GN 발견 시 위반
수역 II (남해){stats.zoneStats.ZONE_II}전 업종-
수역 III (서남해){stats.zoneStats.ZONE_III}전 업종이어도 해역
수역 IV (서해){stats.zoneStats.ZONE_IV}GN, PS, FCPT/OT 발견 시 위반
수역 외{stats.zoneStats.OUTSIDE}-비허가 구역
{/* 5. 위험 분석 */}

5. 위험 평가

위험 유형현재 상태등급
다크베셀 의심{stats.darkSuspect.length}척 10 ? '#dc2626' : '#f59e0b' }}>{stats.darkSuspect.length > 10 ? 'HIGH' : 'MEDIUM'}
수역 외 어선{stats.zoneStats.OUTSIDE}척 0 ? '#dc2626' : '#22c55e' }}>{stats.zoneStats.OUTSIDE > 0 ? 'CRITICAL' : 'NORMAL'}
조업 중 어선{stats.cnOperating.length}척MONITOR
{/* 6. 국적별 현황 */}

6. 국적별 선박 현황 (TOP 10)

{stats.topFlags.map(([flag, count], i) => ( ))}
순위국적척수비율
{i + 1}{flag}{count.toLocaleString()}{pct(count, stats.total)}
{/* 7. 건의사항 */}

7. 건의사항

1. 현재 3월은 전 업종 조업 가능 기간으로, 수역 이탈 및 본선-부속선 분리 중심 감시 권고

2. 다크베셀 의심 {stats.darkSuspect.length}척에 대해 SAR 위성 집중 탐색 요청

3. 수역 외 어선 {stats.zoneStats.OUTSIDE}척에 대해 즉시 현장 확인 필요

4. 4/16 저인망 휴어기 진입 대비 감시 강화 계획 수립 권고

5. 宁波海裕 위망 선단 16척 그룹 위치 상시 추적 유지

{/* Footer */}
본 보고서는 KCG 해양감시 시스템에서 자동 생성된 내부 참고자료입니다. | 생성: {timestamp} | 데이터: 실시간 AIS | 분석: AI 자동분석 엔진 | 【대외비】
); } // Styles const h2Style: React.CSSProperties = { fontSize: 13, color: '#60a5fa', borderLeft: '3px solid #3b82f6', paddingLeft: 8, marginTop: 20 }; const tableStyle: React.CSSProperties = { borderCollapse: 'collapse', width: '100%', fontSize: 10, marginTop: 6 }; const thStyle: React.CSSProperties = { border: '1px solid #334155', padding: '4px 8px', textAlign: 'left', color: '#e2e8f0', fontSize: 9, fontWeight: 700 }; const tdStyle: React.CSSProperties = { border: '1px solid #1e293b', padding: '3px 8px', fontSize: 10 }; const tdBold: React.CSSProperties = { ...tdStyle, fontWeight: 700, color: '#e2e8f0' }; const tdDim: React.CSSProperties = { ...tdStyle, color: '#64748b', fontSize: 9 }; const badgeStyle: React.CSSProperties = { display: 'inline-block', padding: '1px 6px', borderRadius: 3, fontSize: 9, fontWeight: 700, color: '#fff' }; function pct(n: number, total: number): string { if (!total) return '-'; return `${((n / total) * 100).toFixed(1)}%`; }