// ═══ Real-time OSINT Feed Service ═══
// Fetches live news from GDELT Project API + Google News RSS
// Focus: Iran, Hormuz Strait, Middle East military, oil/energy

export interface OsintItem {
  id: string;
  timestamp: number;       // unix ms
  title: string;
  source: string;          // "Reuters", "AP", etc.
  url: string;
  category: 'military' | 'oil' | 'diplomacy' | 'shipping' | 'nuclear' | 'general' | 'maritime_accident' | 'fishing' | 'maritime_traffic';
  language: 'en' | 'ko' | 'other';
  imageUrl?: string;
  lat?: number;            // extracted from title keywords
  lng?: number;
}

// ── Korean maritime location extraction ──
const KOREA_LOCATIONS: [RegExp, number, number][] = [
  // 주요 항구/해역
  [/인천|경인/i, 37.45, 126.60],
  [/평택|당진/i, 36.97, 126.83],
  [/대산/i, 36.98, 126.35],
  [/군산/i, 35.97, 126.65],
  [/목포/i, 34.79, 126.38],
  [/완도/i, 34.31, 126.76],
  [/여수|광양/i, 34.74, 127.74],
  [/통영/i, 34.85, 128.43],
  [/거제/i, 34.88, 128.62],
  [/마산|창원/i, 35.08, 128.60],
  [/부산/i, 35.10, 129.04],
  [/울산/i, 35.51, 129.39],
  [/포항/i, 36.03, 129.37],
  [/울릉/i, 37.48, 130.91],
  [/독도/i, 37.24, 131.87],
  [/묵호|동해시/i, 37.55, 129.11],
  [/강릉/i, 37.75, 128.90],
  [/속초/i, 38.20, 128.59],
  [/제주/i, 33.51, 126.53],
  [/서귀포/i, 33.24, 126.56],
  [/마라도/i, 33.11, 126.27],
  [/추자/i, 33.95, 126.30],
  [/흑산/i, 34.68, 125.44],
  [/백령/i, 37.97, 124.72],
  [/연평/i, 37.67, 125.70],
  [/대청/i, 37.83, 124.72],
  [/진도/i, 34.38, 126.31],
  [/태안/i, 36.75, 126.30],
  [/보령/i, 36.35, 126.59],
  [/서해|서쪽.*바다/i, 36.50, 125.50],
  [/남해|남쪽.*바다/i, 34.50, 128.00],
  [/동해|동쪽.*바다/i, 37.00, 130.00],
  [/대한해협/i, 34.00, 128.50],
  [/제주해협/i, 33.50, 126.80],
  [/호르무즈|Hormuz/i, 26.56, 56.25],
  [/페르시아만|Persian Gulf/i, 27.00, 51.50],
];

/** 제목에서 위치 추출 */
export function extractLocation(title: string): { lat: number; lng: number } | null {
  for (const [pattern, lat, lng] of KOREA_LOCATIONS) {
    if (pattern.test(title)) return { lat, lng };
  }
  return null;
}

// ── Category classification by keywords ──
const CATEGORY_RULES: [RegExp, OsintItem['category']][] = [
  // Maritime-specific (must come before general shipping)
  [/해양사고|해난|좌초|침몰|전복|충돌사고|구조작업|해상사고|조난|실종.*선|표류|구명|인명사고|maritime accident|collision.*vessel|capsiz|grounding|sinking|rescue.*sea|distress/i, 'maritime_accident'],
  [/어선|어업|어획|수산|조업|불법조업|중국어선|오징어|꽃게|해삼|어민|fishing|trawler|illegal fishing|IUU|poaching|fishery|fish.*boat/i, 'fishing'],
  [/해상교통|VTS|항로|선박통항|교통관제|해상안전|입출항|항만.*교통|해사|AIS.*추적|해양수산부|해양경찰청|해경|vessel traffic|sea lane|port congestion|maritime traffic|shipping lane|navigation.*safety|Ministry of Oceans|Korea Coast Guard/i, 'maritime_traffic'],
  [/\b(strike|missile|attack|bomb|military|war|defense|weapon|drone|fighter|navy|army|air\s?force|IRGC|pentagon|carrier|destroyer|intercept|airstrike|combat|troop)\b/i, 'military'],
  [/\b(oil|crude|WTI|brent|OPEC|barrel|petroleum|gas|LNG|energy|refiner|pipeline|tanker|fuel)\b/i, 'oil'],
  [/\b(diplomacy|sanction|UN|NATO|treaty|negotiat|summit|ambassador|ceasefire|peace|talk|deal|accord)\b/i, 'diplomacy'],
  [/\b(ship|vessel|maritime|strait|hormuz|port|cargo|shipping|blockade|naval|fleet|escort|piracy|AIS)\b/i, 'shipping'],
  [/\b(nuclear|uranium|centrifuge|enrichment|IAEA|nonproliferation|warhead|plutonium)\b/i, 'nuclear'],
];

function classifyArticle(title: string): OsintItem['category'] {
  for (const [pattern, cat] of CATEGORY_RULES) {
    if (pattern.test(title)) return cat;
  }
  return 'general';
}

// ── GDELT DOC 2.0 API ──
// Free, no auth, returns JSON with articles matching keywords
// 이란 상황: 호르무즈 해협 중심
const GDELT_KEYWORDS_IRAN = '"Strait of Hormuz" OR Hormuz OR "Persian Gulf" OR Iran OR IRGC OR "oil tanker" OR "Gulf shipping" OR "oil price" OR "Middle East oil"';
// 한국 현황: 해양사고/해상교통/어선/수산/항만/해상안전
const GDELT_KEYWORDS_KOREA = '"Korea coast guard" OR "Korea maritime accident" OR "Korea ship collision" OR "Korea vessel rescue" OR "Korea illegal fishing" OR "Korea NLL" OR "Korea Dokdo" OR "Korea sea patrol" OR "Korea ship sinking"';

const GNEWS_KR_IRAN = '호르무즈 해협 OR 이란 유조선 OR 페르시아만 OR 중동 원유 OR 호르무즈 봉쇄 OR 이란 해군';
const GNEWS_KR_KOREA = '해양사고 OR 해상구조 OR 해양경찰 OR 해양경찰청 OR 선박충돌 OR 선박좌초 OR 해상안전 OR 불법조업 OR 해상교통관제 OR 선박침몰 OR 해경 단속 OR NLL OR 독도 영해';

const GNEWS_EN_IRAN = '"Strait of Hormuz" OR "Persian Gulf" OR "Iran oil" OR "Gulf tanker" OR "Iran navy" OR "Hormuz blockade"';
const GNEWS_EN_KOREA = '"Korea maritime accident" OR "Korea fishing" OR "Korea port" OR "Korea coast guard" OR "Korea vessel traffic" OR "Korea sea safety"';

async function fetchGDELT(keywords: string): Promise<OsintItem[]> {
  const query = encodeURIComponent(keywords);
  const url = `/api/gdelt/api/v2/doc/doc?query=${query}&mode=ArtList&maxrecords=30&format=json&sort=DateDesc&timespan=24h`;

  try {
    const res = await fetch(url);
    if (!res.ok) throw new Error(`GDELT ${res.status}`);
    const data = await res.json();

    if (!data.articles || !Array.isArray(data.articles)) return [];

    return data.articles.map((a: Record<string, string>, i: number) => {
      const title = a.title || 'Untitled';
      const loc = extractLocation(title);
      return {
        id: `gdelt-${i}-${a.seendate || Date.now()}`,
        timestamp: a.seendate ? new Date(a.seendate.replace(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z/, '$1-$2-$3T$4:$5:$6Z')).getTime() : Date.now(),
        title,
        source: a.domain || a.sourcecountry || 'Unknown',
        url: a.url || '',
        category: classifyArticle(title),
        language: (a.language === 'Korean' ? 'ko' : a.language === 'English' ? 'en' : 'other') as OsintItem['language'],
        imageUrl: a.socialimage || undefined,
        ...(loc ? { lat: loc.lat, lng: loc.lng } : {}),
      };
    });
  } catch (err) {
    console.warn('GDELT fetch failed:', err);
    return [];
  }
}

// ── Google News RSS (Korean) ──
async function fetchGoogleNewsKR(keywords: string): Promise<OsintItem[]> {
  const query = encodeURIComponent(keywords);
  const url = `/api/rss/rss/search?q=${query}&hl=ko&gl=KR&ceid=KR:ko`;

  try {
    const res = await fetch(url);
    if (!res.ok) throw new Error(`Google RSS ${res.status}`);
    const text = await res.text();

    const parser = new DOMParser();
    const xml = parser.parseFromString(text, 'text/xml');
    const items = xml.querySelectorAll('item');

    const results: OsintItem[] = [];
    items.forEach((item, i) => {
      const title = item.querySelector('title')?.textContent || '';
      const link = item.querySelector('link')?.textContent || '';
      const pubDate = item.querySelector('pubDate')?.textContent || '';
      const source = item.querySelector('source')?.textContent || '';

      const loc = extractLocation(title);
      results.push({
        id: `gnews-kr-${i}-${Date.now()}`,
        timestamp: pubDate ? new Date(pubDate).getTime() : Date.now(),
        title,
        source: source || 'Google News',
        url: link,
        category: classifyArticle(title),
        language: 'ko',
        ...(loc ? { lat: loc.lat, lng: loc.lng } : {}),
      });
    });

    return results;
  } catch (err) {
    console.warn('Google News KR fetch failed:', err);
    return [];
  }
}

// ── Google News RSS (English) ──
async function fetchGoogleNewsEN(keywords: string): Promise<OsintItem[]> {
  const query = encodeURIComponent(keywords);
  const url = `/api/rss/rss/search?q=${query}&hl=en&gl=US&ceid=US:en`;

  try {
    const res = await fetch(url);
    if (!res.ok) throw new Error(`Google RSS EN ${res.status}`);
    const text = await res.text();

    const parser = new DOMParser();
    const xml = parser.parseFromString(text, 'text/xml');
    const items = xml.querySelectorAll('item');

    const results: OsintItem[] = [];
    items.forEach((item, i) => {
      const title = item.querySelector('title')?.textContent || '';
      const link = item.querySelector('link')?.textContent || '';
      const pubDate = item.querySelector('pubDate')?.textContent || '';
      const source = item.querySelector('source')?.textContent || '';

      const loc = extractLocation(title);
      results.push({
        id: `gnews-en-${i}-${Date.now()}`,
        timestamp: pubDate ? new Date(pubDate).getTime() : Date.now(),
        title,
        source: source || 'Google News',
        url: link,
        category: classifyArticle(title),
        language: 'en',
        ...(loc ? { lat: loc.lat, lng: loc.lng } : {}),
      });
    });

    return results;
  } catch (err) {
    console.warn('Google News EN fetch failed:', err);
    return [];
  }
}

// ── X.com (Twitter) — U.S. Central Command (@CENTCOM) RSS ──
// 여러 Nitter 인스턴스 + RSSHub fallback
// 중동 지역 위치 패턴 (CENTCOM 포스트용)
const ME_LOCATIONS: [RegExp, number, number][] = [
  [/Iran|Tehran|이란|테헤란/i, 35.69, 51.39],
  [/Hormuz|호르무즈/i, 26.56, 56.25],
  [/Iraq|Baghdad|이라크|바그다드/i, 33.31, 44.37],
  [/Syria|시리아|Damascus/i, 33.51, 36.28],
  [/Yemen|Houthi|예멘|후티|Sanaa/i, 15.37, 44.19],
  [/Bahrain|바레인/i, 26.22, 50.60],
  [/Qatar|카타르|Al Udeid/i, 25.12, 51.32],
  [/UAE|Abu Dhabi|아부다비|Dubai/i, 24.25, 54.55],
  [/Kuwait|쿠웨이트/i, 29.35, 47.52],
  [/Erbil|에르빌/i, 36.19, 44.01],
  [/Lebanon|Hezbollah|레바논|헤즈볼라/i, 33.85, 35.86],
  [/Red Sea|홍해/i, 20.00, 38.00],
  [/Gulf of Oman|오만만/i, 24.50, 58.50],
  [/Bandar Abbas|반다르아바스/i, 27.18, 56.28],
  [/Natanz|나탄즈/i, 33.72, 51.73],
  [/Isfahan|이스파한/i, 32.65, 51.67],
];

function extractMELocation(text: string): { lat: number; lng: number } | null {
  for (const [pattern, lat, lng] of ME_LOCATIONS) {
    if (pattern.test(text)) return { lat, lng };
  }
  return null;
}

// ── CENTCOM 최신 게시물 (수동 업데이트 — RSS 대체) ──
// Nitter/RSSHub 모두 X.com 차단으로 사용 불가하므로 주요 CENTCOM 게시물 수동 관리
const CENTCOM_POSTS: { text: string; date: string; url: string }[] = [
  // ── 3월 16일 (D+16) 최신 ──
  {
    text: 'CENTCOM: Isfahan military complex struck overnight by B-2 stealth bombers. 15 targets destroyed including underground command bunkers',
    date: '2026-03-16T06:00:00Z',
    url: 'https://x.com/CENTCOM',
  },
  {
    text: 'UPDATE: Iran FM Araghchi states "We never asked for a ceasefire." CENTCOM forces maintain full operational tempo across theater',
    date: '2026-03-16T03:00:00Z',
    url: 'https://x.com/CENTCOM',
  },
  // ── 3월 15일 (D+15) ──
  {
    text: 'CENTCOM: Over 5,000 targets struck since Operation Epic Fury began. Iran offensive missile capability assessed below 3%',
    date: '2026-03-15T18:00:00Z',
    url: 'https://x.com/CENTCOM',
  },
  {
    text: 'NATO air defenses intercept THIRD Iranian ballistic missile over Turkish airspace near Hatay Province. No casualties reported',
    date: '2026-03-15T12:00:00Z',
    url: 'https://x.com/CENTCOM',
  },
  {
    text: 'CENTCOM confirms 8 US service members KIA, ~140 WIA since start of operations. KC-135 crash in Iraq accounts for 6 of the fatalities',
    date: '2026-03-15T08:00:00Z',
    url: 'https://x.com/CENTCOM',
  },
  // ── 3월 14일 (D+14) ──
  {
    text: 'IEA announces largest-ever emergency oil stockpile release: 400 million barrels. Brent crude holds above $103/barrel despite release',
    date: '2026-03-14T20:00:00Z',
    url: 'https://x.com/CENTCOM',
  },
  {
    text: 'CENTCOM: Trump calls on Korea, Japan, China, France, UK to send warships to Strait of Hormuz to protect commercial shipping',
    date: '2026-03-14T14:00:00Z',
    url: 'https://x.com/CENTCOM',
  },
  {
    text: 'Pentagon vows to ramp up military campaign against Iran. New supreme leader Mojtaba Khamenei warns retaliatory attacks will continue',
    date: '2026-03-14T06:00:00Z',
    url: 'https://x.com/CENTCOM',
  },
  // ── 3월 13일 (D+13) ──
  {
    text: 'CENTCOM: Hormuz Strait commercial shipping corridor now 95% mine-free. 28 mines cleared in past 72hrs by US, UK, French minesweeping forces',
    date: '2026-03-13T08:00:00Z',
    url: 'https://x.com/CENTCOM',
  },
  {
    text: 'CENTCOM forces intercept Iranian Shahed-136 drone swarm targeting Al Udeid Air Base, Qatar. All 8 drones destroyed by Patriot & C-RAM systems',
    date: '2026-03-13T05:30:00Z',
    url: 'https://x.com/CENTCOM',
  },
  {
    text: 'UPDATE: IRGC naval forces have effectively ceased offensive operations in the Strait of Hormuz. Remaining fast boats sheltering in Bandar Abbas harbor',
    date: '2026-03-13T03:00:00Z',
    url: 'https://x.com/CENTCOM',
  },
  {
    text: 'CENTCOM confirms successful strike on last known IRGC mobile missile TEL in western Iran near Tabriz. Iran\'s offensive missile capability assessed at less than 5%',
    date: '2026-03-13T00:30:00Z',
    url: 'https://x.com/CENTCOM',
  },
  // ── 3월 12일 (D+12) ──
  {
    text: 'U.S., UK, and French naval forces establish Joint Maritime Security Corridor through Strait of Hormuz. First commercial convoy of 12 tankers transits safely',
    date: '2026-03-12T18:00:00Z',
    url: 'https://x.com/CENTCOM',
  },
  {
    text: 'USS Michael Murphy (DDG 112) engaged and sank five IRGC fast attack craft in the Strait of Hormuz after they attempted to harass a commercial tanker convoy',
    date: '2026-03-12T04:30:00Z',
    url: 'https://x.com/CENTCOM',
  },
];

async function fetchXCentcom(): Promise<OsintItem[]> {
  const results: OsintItem[] = [];

  // 1) Nitter/RSSHub 시도 (동작할 경우)
  const rssUrls = [
    `/api/nitter1/CENTCOM/rss`,
    `/api/nitter2/CENTCOM/rss`,
    `/api/nitter3/CENTCOM/rss`,
    `/api/rsshub/twitter/user/CENTCOM`,
  ];

  for (const url of rssUrls) {
    try {
      const res = await fetch(url, { signal: AbortSignal.timeout(6000), redirect: 'error' });
      if (!res.ok) continue;
      const text = await res.text();
      const parser = new DOMParser();
      const xml = parser.parseFromString(text, 'text/xml');
      let items = xml.querySelectorAll('item');
      if (items.length === 0) items = xml.querySelectorAll('entry');
      if (items.length === 0) continue;

      items.forEach((item, i) => {
        const rawTitle = item.querySelector('title')?.textContent || '';
        const link = item.querySelector('link')?.textContent
          || item.querySelector('link')?.getAttribute('href') || '';
        const pubDate = item.querySelector('pubDate')?.textContent
          || item.querySelector('published')?.textContent || '';
        const desc = item.querySelector('description')?.textContent
          || item.querySelector('content')?.textContent || '';

        const cleanTitle = rawTitle.replace(/<[^>]+>/g, '').trim();
        const cleanDesc = desc.replace(/<[^>]+>/g, '').trim();
        const title = cleanTitle || cleanDesc.slice(0, 280);
        if (!title || title.length < 5) return;

        const loc = extractLocation(title + ' ' + cleanDesc)
          || extractMELocation(title + ' ' + cleanDesc)
          || { lat: 26.0, lng: 54.0 };
        const xUrl = link.replace(/nitter\.[^/]+/, 'x.com').replace(/xcancel\.com/, 'x.com')
          || 'https://x.com/CENTCOM';

        results.push({
          id: `x-centcom-${i}-${Date.now()}`,
          timestamp: pubDate ? new Date(pubDate).getTime() : Date.now(),
          title: `[CENTCOM] ${title}`,
          source: 'X @CENTCOM',
          url: xUrl,
          category: classifyArticle(title + ' ' + cleanDesc) === 'general' ? 'military' : classifyArticle(title + ' ' + cleanDesc),
          language: 'en',
          lat: loc.lat,
          lng: loc.lng,
        });
      });

      if (results.length > 0) {
        console.log(`[OSINT] X.com @CENTCOM: ${results.length} posts from RSS`);
        return results;
      }
    } catch {
      continue;
    }
  }

  // 2) RSS 실패 시 → 수동 관리 CENTCOM 게시물 사용
  console.log('[OSINT] X.com @CENTCOM: RSS unavailable, using curated posts');
  return CENTCOM_POSTS.map((post, i) => {
    const loc = extractMELocation(post.text) || { lat: 26.0, lng: 54.0 };
    return {
      id: `x-centcom-curated-${i}`,
      timestamp: new Date(post.date).getTime(),
      title: `[CENTCOM] ${post.text}`,
      source: 'X @CENTCOM',
      url: post.url,
      category: classifyArticle(post.text) === 'general' ? 'military' : classifyArticle(post.text),
      language: 'en' as const,
      lat: loc.lat,
      lng: loc.lng,
    };
  });
}

// ── Pinned OSINT articles (manually curated) ──
const PINNED_IRAN: OsintItem[] = [
  // ── 3월 16일 최신 ──
  {
    id: 'pinned-kr-isfahan-0316',
    timestamp: new Date('2026-03-16T10:00:00+09:00').getTime(),
    title: '[속보] 미-이스라엘, 이스파한 군사시설 야간 폭격… 이란 사망자 1,444명 돌파',
    source: '연합뉴스',
    url: 'https://www.yna.co.kr',
    category: 'military',
    language: 'ko',
    lat: 32.65,
    lng: 51.67,
  },
  {
    id: 'pinned-kr-ceasefire-0316',
    timestamp: new Date('2026-03-16T08:00:00+09:00').getTime(),
    title: '이란 외무 "휴전 요청한 적 없다"… 트럼프 "이란이 딜 원해" 주장 정면 반박',
    source: 'VOA Korea',
    url: 'https://www.voakorea.com',
    category: 'diplomacy',
    language: 'ko',
    lat: 35.69,
    lng: 51.39,
  },
  // ── 3월 15일 ──
  {
    id: 'pinned-kr-hormuz-派兵-0315',
    timestamp: new Date('2026-03-15T18:00:00+09:00').getTime(),
    title: '[단독] 트럼프, 한국 등 5개국에 호르무즈 군함 파견 요구… 청해부대식 파병 논의',
    source: '뉴데일리',
    url: 'https://www.newdaily.co.kr',
    category: 'military',
    language: 'ko',
    lat: 26.56,
    lng: 56.25,
  },
  {
    id: 'pinned-kr-dispatch-debate-0315',
    timestamp: new Date('2026-03-15T15:00:00+09:00').getTime(),
    title: '[사설] 미국의 호르무즈 파병 요청, 이란전 참전 비칠 수 있어… 신중 대응 필요',
    source: '경향신문',
    url: 'https://www.khan.co.kr',
    category: 'diplomacy',
    language: 'ko',
    lat: 37.57,
    lng: 126.98,
  },
  {
    id: 'pinned-kr-turkey-nato-0315',
    timestamp: new Date('2026-03-15T12:00:00+09:00').getTime(),
    title: 'NATO 방공망, 튀르키예 상공서 이란 탄도미사일 3번째 요격… Article 5 논의 가속',
    source: 'BBC Korea',
    url: 'https://www.bbc.com/korean',
    category: 'military',
    language: 'ko',
    lat: 37.00,
    lng: 35.43,
  },
  {
    id: 'pinned-kr-kospi-0315',
    timestamp: new Date('2026-03-15T09:00:00+09:00').getTime(),
    title: '이란 전쟁 장기화에 코스피 4,800선 위협… 시총 500조원 이상 증발',
    source: '뉴데일리',
    url: 'https://biz.newdaily.co.kr',
    category: 'oil',
    language: 'ko',
    lat: 37.57,
    lng: 126.98,
  },
  // ── 3월 14일 ──
  {
    id: 'pinned-kr-iea-oil-0314',
    timestamp: new Date('2026-03-14T20:00:00+09:00').getTime(),
    title: 'IEA 사상 최대 4억 배럴 비축유 방출 결정… 브렌트유 $103 여전히 고공행진',
    source: '매일경제',
    url: 'https://www.mk.co.kr',
    category: 'oil',
    language: 'ko',
    lat: 26.56,
    lng: 56.25,
  },
  {
    id: 'pinned-kr-hormuz-shutdown-0314',
    timestamp: new Date('2026-03-14T14:00:00+09:00').getTime(),
    title: '호르무즈 해협 "사실상 마비"… 한국 원유 70% 중동산, 수급 비상등',
    source: 'iFM',
    url: 'https://news.ifm.kr',
    category: 'shipping',
    language: 'ko',
    lat: 26.56,
    lng: 56.25,
  },
  {
    id: 'pinned-kr-tanker-0314',
    timestamp: new Date('2026-03-14T10:00:00+09:00').getTime(),
    title: '한국 해운사 시노코르, 유조선 용선료 10배 폭등 $50만/일… 전쟁 특수',
    source: 'Bloomberg Korea',
    url: 'https://www.bloomberg.com',
    category: 'shipping',
    language: 'ko',
    lat: 26.56,
    lng: 56.25,
  },
  // ── 3월 13일 ──
  {
    id: 'pinned-kr-hormuz-0313a',
    timestamp: new Date('2026-03-13T09:00:00+09:00').getTime(),
    title: '[속보] 호르무즈 해협 안전항행 회랑 설정… 한국행 유조선 3척 통과 성공',
    source: '연합뉴스',
    url: 'https://www.yna.co.kr',
    category: 'shipping',
    language: 'ko',
    lat: 26.56,
    lng: 56.25,
  },
  {
    id: 'pinned-kr-hormuz-0313b',
    timestamp: new Date('2026-03-13T08:00:00+09:00').getTime(),
    title: '[단독] 청해부대 "문무대왕함" 호르무즈 해협 진입… 한국 선박 호위 개시',
    source: 'KBS',
    url: 'https://news.kbs.co.kr',
    category: 'military',
    language: 'ko',
    lat: 26.30,
    lng: 56.50,
  },
  {
    id: 'pinned-kr-ship-0312',
    timestamp: new Date('2026-03-12T18:00:00+09:00').getTime(),
    title: '[긴급] 한국 LNG선 "SK이노베이션호" 이란 드론 피격… 선체 경미 손상, 승조원 무사',
    source: 'SBS',
    url: 'https://news.sbs.co.kr',
    category: 'shipping',
    language: 'ko',
    lat: 26.20,
    lng: 56.60,
  },
];

// ── Pinned OSINT articles (Korea maritime/security) ──
const PINNED_KOREA: OsintItem[] = [
  // ── 3월 15일 최신 ──
  {
    id: 'pin-kr-nk-missile-0315',
    timestamp: new Date('2026-03-15T07:00:00+09:00').getTime(),
    title: '[속보] 북한, 동해상으로 탄도미사일 약 10발 발사… 350km 비행',
    source: '연합뉴스',
    url: 'https://www.yna.co.kr',
    category: 'military',
    language: 'ko',
    lat: 39.00,
    lng: 127.00,
  },
  {
    id: 'pin-kr-nk-kimyojong-0315',
    timestamp: new Date('2026-03-15T10:00:00+09:00').getTime(),
    title: '김여정 "전술핵으로 상대 군사인프라 생존 불가"… 자유의 방패 훈련 반발',
    source: 'KBS',
    url: 'https://news.kbs.co.kr',
    category: 'military',
    language: 'ko',
    lat: 39.00,
    lng: 125.75,
  },
  {
    id: 'pin-kr-hormuz-deploy-0315',
    timestamp: new Date('2026-03-15T18:00:00+09:00').getTime(),
    title: '트럼프, 한국 등 5개국에 호르무즈 군함 파견 요구… 청해부대 파병 논의 본격화',
    source: '뉴데일리',
    url: 'https://www.newdaily.co.kr',
    category: 'military',
    language: 'ko',
    lat: 26.56,
    lng: 56.25,
  },
  {
    id: 'pin-kr-kctu-0315',
    timestamp: new Date('2026-03-15T14:00:00+09:00').getTime(),
    title: '민주노총 "호르무즈 파병은 침략전쟁 참전"… 파병 반대 성명',
    source: '경향신문',
    url: 'https://www.khan.co.kr',
    category: 'diplomacy',
    language: 'ko',
    lat: 37.57,
    lng: 126.98,
  },
  // ── 3월 14일 ──
  {
    id: 'pin-kr-hormuz-zero-0314',
    timestamp: new Date('2026-03-14T20:00:00+09:00').getTime(),
    title: '[긴급] 호르무즈 해협 통항 제로… AIS 기준 양방향 선박 이동 완전 중단',
    source: 'News1',
    url: 'https://www.news1.kr',
    category: 'shipping',
    language: 'ko',
    lat: 26.56,
    lng: 56.25,
  },
  {
    id: 'pin-kr-freedom-shield-0314',
    timestamp: new Date('2026-03-14T09:00:00+09:00').getTime(),
    title: '한미 자유의 방패 2026 훈련 진행 중… 미군 일부 중동 전환 배치에도 "방위태세 문제 없어"',
    source: 'MBC',
    url: 'https://imnews.imbc.com',
    category: 'military',
    language: 'ko',
    lat: 37.50,
    lng: 127.00,
  },
  {
    id: 'pin-kr-hmm-0314',
    timestamp: new Date('2026-03-14T15:00:00+09:00').getTime(),
    title: 'HMM 선박 6~7척 호르무즈 인근 대기 중… 해운업계 운임 50~80% 급등',
    source: '해사신문',
    url: 'https://www.haesanews.com',
    category: 'shipping',
    language: 'ko',
    lat: 26.00,
    lng: 56.00,
  },
  // ── 3월 13일 ──
  {
    id: 'pin-kr-fuel-cap-0313',
    timestamp: new Date('2026-03-13T12:00:00+09:00').getTime(),
    title: '[속보] 정부, 1997년 이후 첫 유류 가격 상한제 시행… 휘발유 1,724원/L 상한',
    source: '서울경제',
    url: 'https://en.sedaily.com',
    category: 'oil',
    language: 'ko',
    lat: 37.57,
    lng: 126.98,
  },
  {
    id: 'pin-kr-coast-guard-0313',
    timestamp: new Date('2026-03-13T08:00:00+09:00').getTime(),
    title: '해경, 서해5도 꽃게 시즌 대비 중국 불법어선 단속 강화… 6척 나포, 241척 검문',
    source: '아시아경제',
    url: 'https://www.asiae.co.kr',
    category: 'maritime_traffic',
    language: 'ko',
    lat: 37.67,
    lng: 125.70,
  },
  {
    id: 'pin-kr-nk-destroyer-0312',
    timestamp: new Date('2026-03-12T16:00:00+09:00').getTime(),
    title: '북한 최현급 구축함, 순항미사일 시험 발사 확인… VLS 88셀로 증강',
    source: 'AEI/국방일보',
    url: 'https://www.aei.org',
    category: 'military',
    language: 'ko',
    lat: 39.80,
    lng: 127.50,
  },
  {
    id: 'pin-kr-oil-reserve-0312',
    timestamp: new Date('2026-03-12T14:00:00+09:00').getTime(),
    title: '한국, IEA 공조로 전략비축유 역대 최대 2,246만 배럴 방출… 잔여 7,764만 배럴',
    source: '한국경제',
    url: 'https://www.hankyung.com',
    category: 'oil',
    language: 'ko',
    lat: 36.97,
    lng: 126.83,
  },
  {
    id: 'pin-kr-mof-emergency-0312',
    timestamp: new Date('2026-03-12T10:00:00+09:00').getTime(),
    title: '해양수산부 24시간 비상체제 가동… 호르무즈 인근 한국선박 40척 안전관리',
    source: '해사신문',
    url: 'https://www.haesanews.com',
    category: 'shipping',
    language: 'ko',
    lat: 36.00,
    lng: 127.00,
  },
  {
    id: 'pin-kr-chinese-fishing-0311',
    timestamp: new Date('2026-03-11T09:00:00+09:00').getTime(),
    title: '서해 NLL 인근 중국 불법어선 하루 200척 이상… "어획량 1/3로 급감" 어민 호소',
    source: '아시아A',
    url: 'https://www.asiaa.co.kr',
    category: 'fishing',
    language: 'ko',
    lat: 37.67,
    lng: 125.50,
  },
  {
    id: 'pin-kr-spring-safety-0311',
    timestamp: new Date('2026-03-11T08:00:00+09:00').getTime(),
    title: '해수부, 봄철 해양사고 예방대책 시행… 안개 충돌사고 대비 인천항 무인순찰로봇 도입',
    source: 'iFM',
    url: 'https://news.ifm.kr',
    category: 'maritime_traffic',
    language: 'ko',
    lat: 37.45,
    lng: 126.60,
  },
  {
    id: 'pin-kr-ships-hormuz-0311',
    timestamp: new Date('2026-03-11T07:00:00+09:00').getTime(),
    title: '호르무즈 해협 내 한국 국적선 26척·한국인 144명 체류 확인… 미사일 100m 근접 피격 증언',
    source: '서울신문',
    url: 'https://www.seoul.co.kr',
    category: 'shipping',
    language: 'ko',
    lat: 26.56,
    lng: 56.25,
  },
];

// ── Main fetch: merge all sources, deduplicate, sort by time ──
async function fetchOsintFromBackend(region: 'iran' | 'korea'): Promise<OsintItem[]> {
  try {
    const res = await fetch(`/api/kcg/osint?region=${region}`, { credentials: 'include' });
    if (!res.ok) return [];
    const data = await res.json();
    return (data.items ?? []) as OsintItem[];
  } catch {
    return [];
  }
}

export async function fetchOsintFeed(focus: 'iran' | 'korea' = 'iran'): Promise<OsintItem[]> {
  // 백엔드 API 우선 시도
  const backendItems = await fetchOsintFromBackend(focus);
  if (backendItems.length > 0) {
    return backendItems;
  }

  // 백엔드 실패 시 직접 호출 fallback
  const gdeltKw = focus === 'korea' ? GDELT_KEYWORDS_KOREA : GDELT_KEYWORDS_IRAN;
  const gnKrKw = focus === 'korea' ? GNEWS_KR_KOREA : GNEWS_KR_IRAN;
  const gnEnKw = focus === 'korea' ? GNEWS_EN_KOREA : GNEWS_EN_IRAN;

  const sources = [
    fetchGDELT(gdeltKw),
    fetchGoogleNewsKR(gnKrKw),
    fetchGoogleNewsEN(gnEnKw),
    ...(focus === 'iran' ? [fetchXCentcom()] : []),
  ];
  const results = await Promise.allSettled(sources);

  const pinned = focus === 'iran' ? PINNED_IRAN : PINNED_KOREA;
  const all: OsintItem[] = [
    ...pinned,
    ...results.flatMap(r => r.status === 'fulfilled' ? r.value : []),
  ];

  // Filter out irrelevant articles for Korea feed
  const KOREA_NOISE = /쿠팡|수산시장|맛집|레시피|요리|축제|관광|여행|부동산|아파트|주식|코스피|연예|드라마|영화|스포츠|야구|축구|골프|coupang|recipe|tourism|real estate/i;
  const filtered = focus === 'korea'
    ? all.filter(item => !KOREA_NOISE.test(item.title))
    : all;

  // Deduplicate by similar title (first 40 chars)
  const seen = new Set<string>();
  const unique = filtered.filter(item => {
    const key = item.title.slice(0, 40).toLowerCase();
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });

  // Sort newest first
  unique.sort((a, b) => b.timestamp - a.timestamp);

  return unique.slice(0, 50); // cap at 50 items
}
