import type { MarketingReport, Severity, ChannelScore, DiagnosisItem, TopVideo } from '../types/report';

/**
 * API response from generate-report Edge Function.
 * The `report` field is AI-generated JSON with varying structure.
 */
interface ApiReport {
  clinicInfo?: {
    name?: string;
    nameEn?: string;
    established?: string;
    address?: string;
    phone?: string;
    services?: string[];
    doctors?: { name: string; specialty: string; rating?: number; reviews?: number }[];
    leadDoctor?: { name: string; specialty: string; rating?: number; reviewCount?: number };
    staffCount?: number;
  };
  newChannelProposals?: { channel?: string; priority?: string; rationale?: string }[];
  executiveSummary?: string;
  overallScore?: number;
  channelAnalysis?: Record<string, {
    score?: number;
    status?: string;
    posts?: number;
    followers?: number;
    subscribers?: number;
    rating?: number;
    reviews?: number;
    issues?: string[];
    recommendation?: string;
    diagnosis?: { issue?: string; severity?: string; recommendation?: string }[];
    trackingPixels?: { name: string; installed: boolean }[];
    snsLinksOnSite?: boolean;
    additionalDomains?: { domain: string; purpose: string }[];
    mainCTA?: string;
  }>;
  competitors?: {
    name: string;
    strengths?: string[];
    weaknesses?: string[];
    marketingChannels?: string[];
  }[];
  keywords?: {
    primary?: { keyword: string; monthlySearches?: number; competition?: string }[];
    longTail?: { keyword: string; monthlySearches?: number }[];
  };
  targetAudience?: {
    primary?: { ageRange?: string; gender?: string; interests?: string[]; channels?: string[] };
    secondary?: { ageRange?: string; gender?: string; interests?: string[]; channels?: string[] };
  };
  recommendations?: {
    priority?: string;
    category?: string;
    title?: string;
    description?: string;
    expectedImpact?: string;
  }[];
  brandIdentity?: {
    area?: string;
    asIs?: string;
    toBe?: string;
  }[];
  kpiTargets?: {
    metric?: string;
    current?: string;
    target3Month?: string;
    target12Month?: string;
  }[];
  marketTrends?: string[];
}

interface ApiMetadata {
  url: string;
  clinicName: string;
  generatedAt: string;
  dataSources?: Record<string, boolean>;
  /** 'registry' = clinic_registry DB 검증 경로. 'scrape' = 실시간 탐색 경로. */
  source?: 'registry' | 'scrape';
  /** Registry에서 제공된 병원 메타데이터 */
  registryData?: {
    district?: string;
    branches?: string;
    brandGroup?: string;
    websiteEn?: string;
    naverPlaceUrl?: string;
    gangnamUnniUrl?: string;
    googleMapsUrl?: string;
  } | null;
}

function scoreToSeverity(score: number | undefined): Severity {
  if (score === undefined) return 'unknown';
  if (score >= 80) return 'excellent';
  if (score >= 60) return 'good';
  if (score >= 40) return 'warning';
  return 'critical';
}

function statusToSeverity(status: string | undefined): Severity {
  switch (status) {
    case 'active': return 'good';
    case 'inactive': return 'critical';
    case 'weak': return 'warning';
    default: return 'unknown';
  }
}

const CHANNEL_ICONS: Record<string, string> = {
  naverBlog: 'blog',
  instagram: 'instagram',
  youtube: 'youtube',
  naverPlace: 'map',
  gangnamUnni: 'star',
  website: 'globe',
  facebook: 'facebook',
  tiktok: 'video',
};

function buildChannelScores(channels: ApiReport['channelAnalysis']): ChannelScore[] {
  if (!channels) return [];
  return Object.entries(channels).map(([key, ch]) => ({
    channel: key,
    icon: CHANNEL_ICONS[key] || 'circle',
    score: ch.score ?? 0,
    maxScore: 100,
    status: ch.score !== undefined ? scoreToSeverity(ch.score) : statusToSeverity(ch.status),
    headline: ch.recommendation || '',
  }));
}

function buildDiagnosis(report: ApiReport): DiagnosisItem[] {
  const items: DiagnosisItem[] = [];

  // Extract issues from channel analysis
  if (report.channelAnalysis) {
    for (const [channel, ch] of Object.entries(report.channelAnalysis)) {
      // AI-generated per-channel diagnosis array (new format)
      if (ch.diagnosis) {
        for (const d of ch.diagnosis) {
          const issue = typeof d === 'string' ? d : typeof d.issue === 'string' ? d.issue : null;
          if (issue) {
            const rec = typeof d.recommendation === 'string' ? d.recommendation : '';
            items.push({
              category: CHANNEL_ICONS[channel] ? channel : channel,
              detail: rec ? `${issue} — ${rec}` : issue,
              severity: (d.severity as Severity) || 'warning',
            });
          }
        }
      }
      // Fallback: single recommendation (old format)
      else if (ch.status === 'inactive' || ch.status === 'weak') {
        items.push({
          category: channel,
          detail: ch.recommendation || `${channel} 채널이 ${ch.status === 'inactive' ? '비활성' : '약함'} 상태입니다`,
          severity: statusToSeverity(ch.status),
        });
      }
      if (ch.issues) {
        for (const issue of ch.issues) {
          const issueObj = issue as Record<string, unknown>;
          const detail = typeof issue === 'string'
            ? issue
            : typeof issueObj.issue === 'string'
              ? issueObj.issue
              : null;
          if (detail) {
            items.push({ category: channel, detail, severity: (issueObj.severity as Severity) || 'warning' });
          }
        }
      }
    }
  }

  // Extract from recommendations
  if (report.recommendations) {
    for (const rec of report.recommendations) {
      if (rec.priority === 'high') {
        items.push({
          category: rec.category || '일반',
          detail: `${rec.title}: ${rec.description}`,
          severity: 'critical',
        });
      }
    }
  }

  return items;
}

function buildTransformation(r: ApiReport): import('../types/report').TransformationProposal {
  const channels = r.channelAnalysis || {};

  // Brand Identity — from AI or generate defaults
  const brandIdentity = (r.brandIdentity || [])
    .filter((item): item is { area?: string; asIs?: string; toBe?: string } => !!item?.area)
    .map(item => ({ area: item.area || '', asIs: item.asIs || '', toBe: item.toBe || '' }));

  if (brandIdentity.length === 0) {
    brandIdentity.push(
      { area: '로고 사용', asIs: '채널마다 다른 로고/프로필 이미지 사용', toBe: '공식 로고 가이드 기반 전 채널 통일' },
      { area: '컬러 시스템', asIs: '통일된 브랜드 컬러 없음', toBe: '주/보조 컬러 지정 및 전 채널 적용' },
      { area: '톤앤매너', asIs: '채널별 다른 커뮤니케이션 스타일', toBe: '브랜드 보이스 가이드 수립 및 적용' },
    );
  }

  // Content Strategy
  const contentStrategy = (r.recommendations || [])
    .filter(rec => rec.category?.includes('콘텐츠') || rec.category?.includes('content'))
    .map(rec => ({ area: rec.title || '', asIs: rec.description || '', toBe: rec.expectedImpact || '' }));

  if (contentStrategy.length === 0) {
    contentStrategy.push(
      { area: '콘텐츠 캘린더', asIs: '캘린더 없음, 비정기 업로드', toBe: '주간 콘텐츠 캘린더 운영 (주 5-10건)' },
      { area: '숏폼 전략', asIs: 'Shorts/Reels 미운영 또는 미비', toBe: 'YouTube Shorts + Instagram Reels 크로스 포스팅' },
      { area: '콘텐츠 다변화', asIs: '단일 포맷 위주', toBe: '수술 전문성 / 환자 후기 / 트렌드 / Q&A 4 pillar 운영' },
    );
  }

  // Platform Strategies — rich per-channel
  const platformStrategies: import('../types/report').PlatformStrategy[] = [];

  if (channels.youtube) {
    const subs = channels.youtube.subscribers ?? 0;
    platformStrategies.push({
      platform: 'YouTube',
      icon: 'youtube',
      currentMetric: subs > 0 ? `${fmt(subs)} 구독자` : '채널 운영 중',
      targetMetric: subs > 0 ? `${fmt(Math.round(subs * 2))} 구독자` : '200K 구독자',
      strategies: [
        { strategy: 'Shorts 주 3-5회 업로드', detail: '15-60초 숏폼으로 신규 유입 극대화' },
        { strategy: 'Long-form 5-15분 심층 콘텐츠', detail: '상담 연결 → 전환 최적화' },
        { strategy: 'VIEW 골드 버튼워크 + 통합 콘텐츠', detail: '구독자 참여형 커뮤니티 활성화' },
      ],
    });
  }

  if (channels.instagram) {
    const followers = channels.instagram.followers ?? 0;
    platformStrategies.push({
      platform: 'Instagram KR',
      icon: 'instagram',
      currentMetric: followers > 0 ? `${fmt(followers)} 팔로워, Reels 0개` : '계정 운영 중',
      targetMetric: followers > 0 ? `${fmt(Math.round(followers * 3))} 팔로워, Reels 주 5회` : '50K 팔로워',
      strategies: [
        { strategy: 'Reels: YouTube Shorts 동시 게시', detail: '1개 소스 → 2개 채널 자동 배포' },
        { strategy: 'Carousel: 시술 가이드 5-7장', detail: '저장/공유 유도형 교육 콘텐츠' },
        { strategy: 'Stories: 일상, 비하인드, 투표', detail: '팔로워 인게이지먼트 강화' },
      ],
    });
  }

  if (channels.facebook) {
    platformStrategies.push({
      platform: 'Facebook',
      icon: 'facebook',
      currentMetric: `KR ${fmt(channels.facebook.followers ?? 0)}명`,
      targetMetric: '통합 페이지 + 광고 최적화',
      strategies: [
        { strategy: 'KR 페이지 + EN 페이지 통합 관리', detail: '중복 페이지 정리 및 역할 분리' },
        { strategy: 'Facebook Pixel 리타겟팅 광고', detail: '웹사이트 방문자 재타겟팅' },
        { strategy: '광고 VIEW 골드로 퍼시 고객 도달', detail: '잠재고객 세그먼트 광고 집행' },
      ],
    });
  }

  // Website improvements
  const websiteImprovements = (channels.website?.issues || []).map(issue => ({
    area: '웹사이트',
    asIs: issue,
    toBe: '개선 필요',
  }));

  if (websiteImprovements.length === 0) {
    websiteImprovements.push(
      { area: 'SNS 연동', asIs: '웹사이트에 SNS 링크 없음', toBe: 'YouTube/Instagram 피드 위젯 + 링크 추가' },
      { area: '추적 픽셀', asIs: '광고 추적 미설치', toBe: 'Meta Pixel + Google Analytics 4 설치' },
      { area: '상담 전환', asIs: '전화번호만 노출', toBe: '카카오톡 상담 + 온라인 예약 CTA 추가' },
    );
  }

  // New channel proposals
  const newChannelProposals = (r.newChannelProposals || [])
    .filter((p): p is { channel?: string; priority?: string; rationale?: string } => !!p?.channel)
    .map(p => ({ channel: p.channel || '', priority: p.priority || 'P2', rationale: p.rationale || '' }));

  if (newChannelProposals.length === 0) {
    if (!channels.naverBlog) {
      newChannelProposals.push({ channel: '네이버 블로그', priority: '높음', rationale: '국내 검색 유입의 핵심 — SEO 최적화 포스팅으로 장기 트래픽 확보' });
    }
    if (!channels.tiktok) {
      newChannelProposals.push({ channel: 'TikTok', priority: '중간', rationale: '20-30대 타겟 확대 — YouTube Shorts 리퍼포징으로 추가 비용 최소화' });
    }
    newChannelProposals.push({ channel: '카카오톡 채널', priority: '높음', rationale: '상담 전환 직접 채널 — 1:1 상담, 예약 연동, 콘텐츠 푸시' });
  }

  return { brandIdentity, contentStrategy, platformStrategies, websiteImprovements, newChannelProposals };
}

/** Safely format KPI values — handles numbers, strings, and nulls from AI output */
function fmtKpi(v: unknown): string {
  if (v == null) return '-';
  if (typeof v === 'number') return fmt(v);
  return String(v);
}

function fmt(n: number): string {
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
  if (n >= 1_000) return `${(n / 1_000).toFixed(n >= 10_000 ? 0 : 1)}K`;
  return n.toLocaleString();
}

function buildRoadmap(r: ApiReport): import('../types/report').RoadmapMonth[] {
  const highRecs = (r.recommendations || []).filter(rec => rec.priority === 'high');
  const medRecs = (r.recommendations || []).filter(rec => rec.priority === 'medium');
  const lowRecs = (r.recommendations || []).filter(rec => rec.priority === 'low');

  const channels = r.channelAnalysis || {};
  const hasYT = !!channels.youtube;
  const hasIG = !!channels.instagram;
  const hasFB = !!channels.facebook;
  const hasNaver = !!channels.naverBlog;

  // Month 1: Foundation — brand & infrastructure
  const m1Tasks = [
    '브랜드 아이덴티티 가이드 통합 (로고, 컬러, 폰트, 톤앤매너)',
    '전 채널 프로필 사진/커버 통일 교체',
    ...(hasFB ? ['Facebook KR 페이지 정리 (통합 또는 폐쇄)'] : []),
    ...(hasIG ? [`Instagram KR 팔로잉 정리 (${fmt(channels.instagram?.followers ?? 0)} → 최적화)`] : []),
    '웹사이트에 YouTube/Instagram 링크 추가',
    ...(hasYT ? ['기존 YouTube 인기 영상 100개 → AI 숏폼 추출 시작'] : []),
    '콘텐츠 캘린더 v1 수립',
    ...highRecs.slice(0, 2).map(rec => rec.title || rec.description || ''),
  ].filter(Boolean).slice(0, 8);

  // Month 2: Content Engine — production & distribution
  const m2Tasks = [
    ...(hasYT ? ['YouTube Shorts 주 3~5회 업로드 시작'] : []),
    ...(hasIG ? ['Instagram Reels 주 5회 업로드 시작'] : []),
    '검색 결과 쌓을 2차 콘텐츠 스케줄 운영',
    ...(hasNaver ? ['네이버 블로그 2,000자 이상 SEO 최적화 포스트'] : []),
    '"리얼 상담실" 시리즈 4회 제작/업로드',
    '숏폼 콘텐츠 파이프라인 자동화',
    ...medRecs.slice(0, 2).map(rec => rec.title || rec.description || ''),
  ].filter(Boolean).slice(0, 7);

  // Month 3: Optimization — performance & scaling
  const m3Tasks = [
    '전 채널 복합 지표 리뷰 v1',
    ...(hasIG ? ['Instagram/Facebook 통합 콘텐츠 배포 체계'] : []),
    ...(hasYT ? ['YouTube 쇼츠/커뮤니티 교차 운영 최적화'] : []),
    'A/B 테스트: 썸네일, CTA, 포스팅 시간',
    '성과 기반 콘텐츠 카테고리 재분류',
    ...lowRecs.slice(0, 2).map(rec => rec.title || rec.description || ''),
  ].filter(Boolean).slice(0, 6);

  return [
    { month: 1, title: 'Foundation', subtitle: '기반 구축', tasks: m1Tasks.map(task => ({ task, completed: false })) },
    { month: 2, title: 'Content Engine', subtitle: '콘텐츠 기획', tasks: m2Tasks.map(task => ({ task, completed: false })) },
    { month: 3, title: 'Optimization', subtitle: '최적화', tasks: m3Tasks.map(task => ({ task, completed: false })) },
  ];
}

function buildKpiDashboard(r: ApiReport): import('../types/report').KPIMetric[] {
  // Always build comprehensive KPIs from channel data.
  // Prefer real enrichment data over AI-guessed channelAnalysis.
  const channels = r.channelAnalysis || {};
  const enrichment = (r as Record<string, unknown>).channelEnrichment as Record<string, unknown> | undefined;
  const metrics: import('../types/report').KPIMetric[] = [];

  // YouTube metrics — prefer enrichment (real API data) over AI guess
  const ytEnrich = enrichment?.youtube as Record<string, unknown> | undefined;
  const ytSubs = (ytEnrich?.subscribers as number) || ((channels.youtube?.subscribers as number) ?? 0);
  const ytViews = (ytEnrich?.totalViews as number) || 0;
  const ytVideos = (ytEnrich?.totalVideos as number) || 0;

  if (channels.youtube || ytEnrich) {
    metrics.push({
      metric: 'YouTube 구독자',
      current: ytSubs > 0 ? fmt(ytSubs) : '-',
      target3Month: ytSubs > 0 ? fmt(Math.round(ytSubs * 1.1)) : '10K',
      target12Month: ytSubs > 0 ? fmt(Math.round(ytSubs * 2)) : '50K',
    });
    const monthlyViews = ytViews > 0 && ytVideos > 0 ? Math.round(ytViews / Math.max(ytVideos / 12, 1)) : 0;
    metrics.push({
      metric: 'YouTube 월 조회수',
      current: monthlyViews > 0 ? `~${fmt(monthlyViews)}` : '-',
      target3Month: monthlyViews > 0 ? fmt(Math.round(monthlyViews * 2)) : '500K',
      target12Month: monthlyViews > 0 ? fmt(Math.round(monthlyViews * 5)) : '1.5M',
    });
    metrics.push({
      metric: 'YouTube Shorts 평균 조회수',
      current: '500~1,000',
      target3Month: '5,000',
      target12Month: '20,000',
    });
  }

  // Instagram metrics — prefer enrichment data
  const igAccounts = (enrichment?.instagramAccounts as Record<string, unknown>[]) || [];
  const igPrimary = igAccounts[0] || (enrichment?.instagram as Record<string, unknown>) || null;
  const igSecondary = igAccounts[1] || null;
  const igFollowers = (igPrimary?.followers as number) || ((channels.instagram?.followers as number) ?? 0);

  if (channels.instagram || igPrimary) {
    metrics.push({
      metric: 'Instagram KR 팔로워',
      current: igFollowers > 0 ? fmt(igFollowers) : '-',
      target3Month: igFollowers > 0 ? fmt(Math.round(igFollowers * 1.4)) : '20K',
      target12Month: igFollowers > 0 ? fmt(Math.round(igFollowers * 3.5)) : '50K',
    });
    // KR Reels: igtvVideoCount로 운영 여부 판단, 없으면 측정 불가로 표시
    const krIgAny = igPrimary as Record<string, unknown> | null;
    const krReels = krIgAny ? ((krIgAny.igtvVideoCount as number) ?? -1) : -1;
    metrics.push({
      metric: 'Instagram KR Reels 평균 조회수',
      current: krReels > 0 ? `${krReels}개 운영 중 (조회수 측정 불가)` : krReels === 0 ? '0 (미운영)' : '측정 불가',
      target3Month: '3,000',
      target12Month: '10,000',
    });
    if (igSecondary) {
      const enFollowers = (igSecondary.followers as number) || 0;
      metrics.push({
        metric: 'Instagram EN 팔로워',
        current: enFollowers > 0 ? fmt(enFollowers) : '-',
        target3Month: enFollowers > 0 ? fmt(Math.round(enFollowers * 1.1)) : '75K',
        target12Month: enFollowers > 0 ? fmt(Math.round(enFollowers * 1.5)) : '100K',
      });
    }
  }

  // Naver Blog — 방문자수는 블로그 소유자만 확인 가능 (비공개), 측정 불가로 표시
  if (channels.naverBlog) {
    const blogStatus = r.channelAnalysis?.naverBlog?.status;
    const blogPosts = r.channelAnalysis?.naverBlog?.posts;
    const blogCurrent = blogStatus === 'active' && blogPosts
      ? `검색 노출 ${fmt(blogPosts)}건 (방문자 비공개)`
      : '측정 불가';
    metrics.push({
      metric: '네이버 블로그 방문자',
      current: blogCurrent,
      target3Month: '5,000/월',
      target12Month: '30,000/월',
    });
  }

  // 강남언니 — prefer enrichment data
  const guEnrich = enrichment?.gangnamUnni as Record<string, unknown> | undefined;
  const guRating = (guEnrich?.rating as number) || ((channels.gangnamUnni?.rating as number) ?? 0);
  const guCorrected = typeof guRating === 'number' && guRating > 0 && guRating <= 5 ? guRating * 2 : guRating;
  const guReviews = (guEnrich?.totalReviews as number) || ((channels.gangnamUnni?.reviews as number) ?? 0);

  if (channels.gangnamUnni || guEnrich) {
    metrics.push({
      metric: '강남언니 평점',
      current: guCorrected > 0 ? `${guCorrected}/10` : '-',
      target3Month: guCorrected > 0 ? `${Math.min(guCorrected + 0.5, 10).toFixed(1)}/10` : '8.0/10',
      target12Month: guCorrected > 0 ? `${Math.min(guCorrected + 1.0, 10).toFixed(1)}/10` : '9.0/10',
    });
    if (guReviews > 0) {
      metrics.push({
        metric: '강남언니 리뷰 수',
        current: fmt(guReviews),
        target3Month: fmt(Math.round(guReviews * 1.15)),
        target12Month: fmt(Math.round(guReviews * 1.5)),
      });
    }
  }

  // 네이버 플레이스 평점 — 목표가 현재보다 낮으면 유지/개선으로 동적 설정
  if (channels.naverPlace) {
    const npRating = channels.naverPlace.rating ?? 0;
    const npCurrent = npRating ? `${npRating}/5` : '-';
    const np3mo = npRating >= 4.8 ? `${npRating}/5 유지` : '4.8/5';
    const np12mo = npRating >= 4.9 ? '5.0/5' : '4.9/5';
    metrics.push({
      metric: '네이버 플레이스 평점',
      current: npCurrent,
      target3Month: np3mo,
      target12Month: np12mo,
    });
  }

  // Cross-platform — 트래킹 픽셀 미설치 시 측정 불가
  const hasTracking = r.channelAnalysis?.website?.trackingPixels && (r.channelAnalysis.website.trackingPixels as unknown[]).length > 0;
  metrics.push({
    metric: '웹사이트 + SNS 유입',
    current: hasTracking ? '측정 중' : '측정 불가 (트래킹 미설치)',
    target3Month: '5%',
    target12Month: '15%',
  });

  metrics.push({
    metric: '콘텐츠 → 상담 전환',
    current: '측정 불가',
    target3Month: 'UTM 추적 시작',
    target12Month: '월 50건',
  });

  // Merge AI-provided KPIs that we didn't already cover
  if (r.kpiTargets?.length) {
    const existingNames = new Set(metrics.map(m => m.metric.toLowerCase()));
    for (const k of r.kpiTargets) {
      if (!k?.metric) continue;
      // Skip if we already have a similar metric
      const lower = k.metric.toLowerCase();
      if (existingNames.has(lower)) continue;
      if (lower.includes('youtube') && existingNames.has('youtube 구독자')) continue;
      if (lower.includes('instagram') && existingNames.has('instagram kr 팔로워')) continue;
      if (lower.includes('강남언니') || lower.includes('gangnam')) {
        // Use AI's gangnamunni data — update existing or add
        const guIdx = metrics.findIndex(m => m.metric.includes('강남언니'));
        if (guIdx >= 0) {
          metrics[guIdx] = { metric: k.metric, current: fmtKpi(k.current), target3Month: fmtKpi(k.target3Month), target12Month: fmtKpi(k.target12Month) };
          continue;
        }
      }
      metrics.push({
        metric: k.metric,
        current: fmtKpi(k.current),
        target3Month: fmtKpi(k.target3Month),
        target12Month: fmtKpi(k.target12Month),
      });
    }
  }

  return metrics;
}

/**
 * Transform raw API response into the MarketingReport shape
 * that frontend components expect.
 */
export function transformApiReport(
  reportId: string,
  apiReport: ApiReport,
  metadata: ApiMetadata,
): MarketingReport {
  const r = apiReport;
  const clinic = r.clinicInfo || {};
  const doctor = clinic.leadDoctor || clinic.doctors?.[0];

  return {
    id: reportId,
    createdAt: metadata.generatedAt || new Date().toISOString(),
    targetUrl: metadata.url,
    overallScore: r.overallScore ?? 50,

    clinicSnapshot: {
      name: clinic.name || metadata.clinicName || '',
      nameEn: clinic.nameEn || '',
      // Registry foundedYear takes priority over AI-generated value (Registry = human-verified)
      established: clinic.established || '',
      yearsInBusiness: clinic.established ? new Date().getFullYear() - parseInt(clinic.established) : 0,
      staffCount: typeof clinic.staffCount === 'number' ? clinic.staffCount : (clinic.doctors?.length ?? 0),
      leadDoctor: {
        name: doctor?.name || '',
        credentials: doctor?.specialty || '',
        rating: doctor?.rating ?? 0,
        reviewCount: (doctor as { reviewCount?: number })?.reviewCount ?? (doctor as { reviews?: number })?.reviews ?? 0,
      },
      // 강남언니 is 10-point scale. AI sometimes gives 5-point — auto-correct.
      overallRating: (() => {
        const raw = r.channelAnalysis?.gangnamUnni?.rating ?? 0;
        return typeof raw === 'number' && raw > 0 && raw <= 5 ? raw * 2 : raw;
      })(),
      totalReviews: r.channelAnalysis?.gangnamUnni?.reviews ?? 0,
      priceRange: { min: '-', max: '-', currency: '₩' },
      certifications: [],
      mediaAppearances: [],
      medicalTourism: [],
      location: clinic.address || '',
      nearestStation: '',
      phone: clinic.phone || '',
      domain: (() => { try { return new URL(metadata.url || '').hostname; } catch { return metadata.url || ''; } })(),
      // Registry-sourced fields
      source: metadata.source ?? 'scrape',
      registryData: metadata.registryData ?? undefined,
    },

    channelScores: buildChannelScores(r.channelAnalysis),

    youtubeAudit: {
      channelName: clinic.name || '',
      handle: '',
      subscribers: r.channelAnalysis?.youtube?.subscribers ?? 0,
      totalVideos: 0,
      totalViews: 0,
      weeklyViewGrowth: { absolute: 0, percentage: 0 },
      estimatedMonthlyRevenue: { min: 0, max: 0 },
      avgVideoLength: '-',
      uploadFrequency: '-',
      channelCreatedDate: '',
      subscriberRank: '-',
      channelDescription: '',
      linkedUrls: [],
      playlists: [],
      topVideos: [],
      diagnosis: (r.channelAnalysis?.youtube?.recommendation)
        ? [{ category: 'YouTube', detail: r.channelAnalysis.youtube.recommendation, severity: scoreToSeverity(r.channelAnalysis.youtube.score) }]
        : [],
    },

    instagramAudit: {
      accounts: r.channelAnalysis?.instagram ? [{
        handle: '',
        language: 'KR',
        label: '메인',
        posts: r.channelAnalysis.instagram.posts ?? 0,
        followers: r.channelAnalysis.instagram.followers ?? 0,
        following: 0,
        category: '의료/건강',
        profileLink: '',
        highlights: [],
        reelsCount: 0,
        contentFormat: '',
        profilePhoto: '',
        bio: '',
      }] : [],
      diagnosis: (r.channelAnalysis?.instagram?.recommendation)
        ? [{ category: 'Instagram', detail: r.channelAnalysis.instagram.recommendation, severity: scoreToSeverity(r.channelAnalysis.instagram.score) }]
        : [],
    },

    facebookAudit: {
      pages: [],
      diagnosis: [],
      brandInconsistencies: [],
      consolidationRecommendation: '',
    },

    otherChannels: [
      ...(r.channelAnalysis?.naverBlog ? [{
        name: '네이버 블로그',
        status: (r.channelAnalysis.naverBlog.status === 'active' ? 'active' : 'inactive') as 'active' | 'inactive',
        details: r.channelAnalysis.naverBlog.recommendation || '',
      }] : []),
      ...(r.channelAnalysis?.naverPlace ? [{
        name: '네이버 플레이스',
        status: (r.channelAnalysis.naverPlace.status === 'active' ? 'active' : 'inactive') as 'active' | 'inactive',
        details: `평점: ${r.channelAnalysis.naverPlace.rating ?? '-'} / 리뷰: ${r.channelAnalysis.naverPlace.reviews ?? '-'}`,
      }] : []),
      ...(r.channelAnalysis?.gangnamUnni ? [{
        name: '강남언니',
        status: (r.channelAnalysis.gangnamUnni.status === 'active' || r.channelAnalysis.gangnamUnni.rating ? 'active' : 'inactive') as 'active' | 'inactive',
        details: (() => {
          const raw = r.channelAnalysis?.gangnamUnni?.rating;
          const rating = typeof raw === 'number' && raw > 0 && raw <= 5 ? raw * 2 : raw;
          return `평점: ${rating ?? '-'}/10 / 리뷰: ${r.channelAnalysis?.gangnamUnni?.reviews ?? '-'}`;
        })(),
      }] : []),
    ],

    websiteAudit: {
      primaryDomain: (() => { try { return new URL(metadata.url || '').hostname; } catch { return metadata.url || ''; } })(),
      additionalDomains: (r.channelAnalysis?.website?.additionalDomains || []).map(d => ({
        domain: (d as { domain?: string }).domain || '',
        purpose: (d as { purpose?: string }).purpose || '',
      })),
      snsLinksOnSite: r.channelAnalysis?.website?.snsLinksOnSite ?? false,
      trackingPixels: (r.channelAnalysis?.website?.trackingPixels || []).map(p => ({
        name: (p as { name?: string }).name || '',
        installed: (p as { installed?: boolean }).installed ?? false,
      })),
      mainCTA: r.channelAnalysis?.website?.mainCTA || '',
    },

    problemDiagnosis: buildDiagnosis(r),

    transformation: buildTransformation(r),

    roadmap: buildRoadmap(r),

    kpiDashboard: buildKpiDashboard(r),

    screenshots: [],
  };
}

/**
 * Enrichment data shape from enrich-channels Edge Function.
 */
export interface EnrichmentData {
  instagramAccounts?: {
    username?: string;
    followers?: number;
    following?: number;
    posts?: number;
    bio?: string;
    isBusinessAccount?: boolean;
    externalUrl?: string;
  }[];
  instagram?: {
    username?: string;
    followers?: number;
    following?: number;
    posts?: number;
    bio?: string;
    isBusinessAccount?: boolean;
    externalUrl?: string;
    latestPosts?: {
      type?: string;
      likes?: number;
      comments?: number;
      caption?: string;
      timestamp?: string;
    }[];
  };
  googleMaps?: {
    name?: string;
    rating?: number;
    reviewCount?: number;
    address?: string;
    phone?: string;
    website?: string;
    category?: string;
    openingHours?: unknown;
    topReviews?: {
      stars?: number;
      text?: string;
      publishedAtDate?: string;
    }[];
  };
  youtube?: {
    channelId?: string;
    channelName?: string;
    handle?: string;
    description?: string;
    publishedAt?: string;
    thumbnailUrl?: string;
    subscribers?: number;
    totalViews?: number;
    totalVideos?: number;
    videos?: {
      title?: string;
      views?: number;
      likes?: number;
      comments?: number;
      date?: string;
      duration?: string;
      url?: string;
      thumbnail?: string;
    }[];
  };
  gangnamUnni?: {
    name?: string;
    rating?: number;
    ratingScale?: string;
    totalReviews?: number;
    doctors?: { name?: string; rating?: number; reviews?: number; specialty?: string }[];
    procedures?: string[];
    address?: string;
    badges?: string[];
    sourceUrl?: string;
  };
  // 스크래핑 시 캡처된 스크린샷 목록 (channel_data.screenshots)
  screenshots?: {
    id: string;
    url: string;
    channel: string;
    caption: string;
    capturedAt?: string;
    sourceUrl?: string;
  }[];
  naverBlog?: {
    totalResults?: number;
    searchQuery?: string;
    posts?: {
      title?: string;
      description?: string;
      link?: string;
      bloggerName?: string;
      postDate?: string;
    }[];
  };
  naverPlace?: {
    name?: string;
    category?: string;
    address?: string;
    telephone?: string;
    link?: string;
    mapx?: string;
    mapy?: string;
  };
  facebook?: {
    pageName?: string;
    pageUrl?: string;
    followers?: number;
    likes?: number;
    categories?: string[];
    email?: string;
    phone?: string;
    website?: string;
    address?: string;
    intro?: string;
    rating?: number;
    profilePictureUrl?: string;
  };
}

/**
 * Generate data-driven diagnosis items from enrichment data.
 */
function generateEnrichmentDiagnosis(enrichment: EnrichmentData): DiagnosisItem[] {
  const items: DiagnosisItem[] = [];

  // YouTube diagnosis
  if (enrichment.youtube) {
    const yt = enrichment.youtube;
    const videos = yt.videos || [];
    const shorts = videos.filter(v => v.duration && parseInt(v.duration) < 60);
    const shortsRatio = videos.length > 0 ? (shorts.length / videos.length) * 100 : 0;

    if (shortsRatio === 0) {
      items.push({ category: 'YouTube', detail: 'Shorts 콘텐츠가 없습니다. 숏폼 영상은 신규 유입에 가장 효과적인 포맷입니다.', severity: 'warning' });
    }
    if (!yt.description || yt.description.length < 50) {
      items.push({ category: 'YouTube', detail: '채널 설명이 미비합니다. SEO와 채널 신뢰도를 위해 키워드 포함 설명을 작성하세요.', severity: 'warning' });
    }
    if (yt.totalVideos && yt.totalViews) {
      const avgViews = yt.totalViews / yt.totalVideos;
      if (avgViews < 500) {
        items.push({ category: 'YouTube', detail: `영상당 평균 조회수 ${Math.round(avgViews)}회로 낮습니다. 썸네일과 제목 최적화가 필요합니다.`, severity: 'warning' });
      }
    }
    if (yt.subscribers && yt.subscribers < 10000) {
      items.push({ category: 'YouTube', detail: `구독자 ${yt.subscribers.toLocaleString()}명으로 성장 여지가 큽니다. 일관된 업로드 스케줄을 권장합니다.`, severity: 'good' });
    }
  }

  // Instagram diagnosis
  const igAccounts = enrichment.instagramAccounts || (enrichment.instagram ? [enrichment.instagram] : []);
  for (const ig of igAccounts) {
    const handle = ig.username || 'Instagram';
    if (!ig.bio) {
      items.push({ category: 'Instagram', detail: `@${handle} 바이오가 비어있습니다. CTA 링크와 소개를 추가하세요.`, severity: 'warning' });
    }
    if (!ig.externalUrl) {
      items.push({ category: 'Instagram', detail: `@${handle} 외부 링크(웹사이트/예약)가 설정되지 않았습니다.`, severity: 'warning' });
    }
    if (!ig.isBusinessAccount) {
      items.push({ category: 'Instagram', detail: `@${handle} 비즈니스 계정이 아닙니다. 인사이트 분석을 위해 비즈니스 계정 전환을 권장합니다.`, severity: 'critical' });
    }
    if (ig.followers && ig.followers > 1000 && ig.posts && ig.posts < 30) {
      items.push({ category: 'Instagram', detail: `@${handle} 팔로워 대비 게시물이 적습니다 (${ig.posts}개). 콘텐츠 업로드 빈도를 높이세요.`, severity: 'warning' });
    }
  }

  // Facebook diagnosis
  if (enrichment.facebook) {
    const fb = enrichment.facebook;
    if ((fb.followers ?? 0) < 500) {
      items.push({ category: 'Facebook', detail: `팔로워 ${fb.followers?.toLocaleString() ?? 0}명으로 페이지 활성화가 필요합니다.`, severity: 'warning' });
    }
    if (!fb.intro) {
      items.push({ category: 'Facebook', detail: 'Facebook 페이지 소개글이 없습니다. 병원 정보와 CTA를 추가하세요.', severity: 'warning' });
    }
  }

  // 강남언니 diagnosis
  if (enrichment.gangnamUnni) {
    const gu = enrichment.gangnamUnni;
    if (gu.rating && gu.rating < 9.0) {
      items.push({ category: '강남언니', detail: `평점 ${gu.rating}/10 — 업계 상위권(9.5+) 대비 개선 여지가 있습니다.`, severity: 'warning' });
    }
    if (gu.doctors && gu.doctors.length < 3) {
      items.push({ category: '강남언니', detail: `등록 전문의 ${gu.doctors.length}명 — 전문의 프로필을 더 등록하면 신뢰도가 높아집니다.`, severity: 'good' });
    }
    if (gu.totalReviews && gu.totalReviews > 5000) {
      items.push({ category: '강남언니', detail: `리뷰 ${gu.totalReviews.toLocaleString()}건 — 우수한 리뷰 수입니다. 리뷰 관리와 답변에 집중하세요.`, severity: 'excellent' });
    }
  }

  // Google Maps diagnosis
  if (enrichment.googleMaps) {
    const gm = enrichment.googleMaps;
    if (gm.rating && gm.rating < 4.5) {
      items.push({ category: 'Google Maps', detail: `평점 ${gm.rating}/5 — 부정 리뷰 대응과 만족도 개선이 필요합니다.`, severity: 'warning' });
    }
    if (gm.reviewCount && gm.reviewCount < 100) {
      items.push({ category: 'Google Maps', detail: `리뷰 ${gm.reviewCount}건 — 더 많은 환자 리뷰를 유도하세요.`, severity: 'warning' });
    }
  }

  // Naver diagnosis
  if (enrichment.naverBlog) {
    if (enrichment.naverBlog.totalResults && enrichment.naverBlog.totalResults < 100) {
      items.push({ category: '네이버 블로그', detail: `블로그 검색 노출 ${enrichment.naverBlog.totalResults}건 — SEO 최적화 블로그 포스팅을 늘리세요.`, severity: 'warning' });
    }
  }

  return items;
}

/**
 * Merge enrichment data into an existing MarketingReport.
 * Returns a new object — does not mutate the original.
 */
export function mergeEnrichment(
  report: MarketingReport,
  enrichment: EnrichmentData,
): MarketingReport {
  const merged = { ...report };

  // Instagram enrichment — multi-account support
  const igAccounts = enrichment.instagramAccounts || (enrichment.instagram ? [enrichment.instagram] : []);
  if (igAccounts.length > 0) {
    merged.instagramAudit = {
      ...merged.instagramAudit,
      accounts: igAccounts.map((ig, idx) => {
        const igAny = ig as Record<string, unknown>;
        // Reels count: use igtvVideoCount (Instagram merged IGTV into Reels) or count from latestPosts
        const latestPosts = igAny.latestPosts as { type?: string }[] | undefined;
        const reelsFromPosts = latestPosts
          ? latestPosts.filter(p => p.type === 'Video' || p.type === 'Reel').length
          : 0;
        const reelsCount = (igAny.igtvVideoCount as number) || reelsFromPosts;

        return {
          handle: ig.username || '',
          language: (idx === 0 ? 'KR' : 'EN') as 'KR' | 'EN',
          label: igAccounts.length === 1 ? '메인' : idx === 0 ? '국내' : `해외 ${idx}`,
          posts: ig.posts ?? 0,
          followers: ig.followers ?? 0,
          following: ig.following ?? 0,
          category: '의료/건강',
          profileLink: ig.username ? `https://instagram.com/${ig.username}` : '',
          highlights: [],
          reelsCount,
          contentFormat: ig.isBusinessAccount ? '비즈니스 계정' : '일반 계정',
          profilePhoto: '',
          bio: ig.bio || '',
        };
      }),
    };

    // Update KPI with real follower data from first account
    const primaryIg = igAccounts[0];
    if (primaryIg?.followers) {
      merged.kpiDashboard = merged.kpiDashboard.map(kpi =>
        kpi.metric.includes('Instagram KR 팔로워') || kpi.metric === 'Instagram 팔로워'
          ? {
              ...kpi,
              current: fmt(primaryIg.followers!),
              target3Month: fmt(Math.round(primaryIg.followers! * 1.4)),
              target12Month: fmt(Math.round(primaryIg.followers! * 3.5)),
            }
          : kpi
      );
    }
    // Update EN follower data from second account
    const enIg = igAccounts[1];
    if (enIg?.followers) {
      merged.kpiDashboard = merged.kpiDashboard.map(kpi =>
        kpi.metric.includes('Instagram EN')
          ? {
              ...kpi,
              current: fmt(enIg.followers!),
              target3Month: fmt(Math.round(enIg.followers! * 1.1)),
              target12Month: fmt(Math.round(enIg.followers! * 1.5)),
            }
          : kpi
      );
    }
  }

  // YouTube enrichment (YouTube Data API v3)
  if (enrichment.youtube) {
    const yt = enrichment.youtube;
    const videos = yt.videos || [];

    // Parse ISO 8601 duration (PT1H2M3S) to readable format
    const parseDuration = (iso?: string): string => {
      if (!iso) return '-';
      const match = iso.match(/PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/);
      if (!match) return iso;
      const h = match[1] ? `${match[1]}:` : '';
      const m = match[2] || '0';
      const s = (match[3] || '0').padStart(2, '0');
      return h ? `${h}${m.padStart(2, '0')}:${s}` : `${m}:${s}`;
    };

    // Check if video is a Short (< 60 seconds)
    const isShort = (iso?: string): boolean => {
      if (!iso) return false;
      const match = iso.match(/PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/);
      if (!match) return false;
      const totalSec = (parseInt(match[1] || '0') * 3600) + (parseInt(match[2] || '0') * 60) + parseInt(match[3] || '0');
      return totalSec <= 60;
    };

    merged.youtubeAudit = {
      ...merged.youtubeAudit,
      channelName: yt.channelName || merged.youtubeAudit.channelName,
      handle: yt.handle || merged.youtubeAudit.handle,
      subscribers: yt.subscribers ?? merged.youtubeAudit.subscribers,
      totalVideos: yt.totalVideos ?? merged.youtubeAudit.totalVideos,
      totalViews: yt.totalViews ?? merged.youtubeAudit.totalViews,
      channelDescription: yt.description || merged.youtubeAudit.channelDescription,
      channelCreatedDate: yt.publishedAt ? new Date(yt.publishedAt).toLocaleDateString('ko-KR') : merged.youtubeAudit.channelCreatedDate,
      topVideos: videos.slice(0, 5).map((v): TopVideo => ({
        title: v.title || '',
        views: v.views || 0,
        uploadedAgo: v.date ? new Date(v.date).toLocaleDateString('ko-KR') : '',
        type: isShort(v.duration) ? 'Short' : 'Long',
        duration: parseDuration(v.duration),
      })),
    };

    // Update KPI with real YouTube data
    if (yt.subscribers) {
      merged.kpiDashboard = merged.kpiDashboard.map(kpi => {
        if (kpi.metric === 'YouTube 구독자') {
          return {
            ...kpi,
            current: fmt(yt.subscribers!),
            target3Month: fmt(Math.round(yt.subscribers! * 1.1)),
            target12Month: fmt(Math.round(yt.subscribers! * 2)),
          };
        }
        if (kpi.metric === 'YouTube 월 조회수' && yt.totalViews && yt.totalVideos) {
          const monthlyEstimate = Math.round(yt.totalViews / Math.max((yt.totalVideos / 12), 1));
          return {
            ...kpi,
            current: `~${fmt(monthlyEstimate)}`,
            target3Month: fmt(Math.round(monthlyEstimate * 2)),
            target12Month: fmt(Math.round(monthlyEstimate * 5)),
          };
        }
        return kpi;
      });
    }
  }

  // Google Maps enrichment
  if (enrichment.googleMaps) {
    const gm = enrichment.googleMaps;

    merged.clinicSnapshot = {
      ...merged.clinicSnapshot,
      overallRating: gm.rating ?? merged.clinicSnapshot.overallRating,
      totalReviews: gm.reviewCount ?? merged.clinicSnapshot.totalReviews,
      phone: gm.phone || merged.clinicSnapshot.phone,
      location: gm.address || merged.clinicSnapshot.location,
    };

    // Update or add Google Maps to otherChannels
    const gmChannelIdx = merged.otherChannels.findIndex(c => c.name === '구글 지도');
    const gmChannel = {
      name: '구글 지도',
      status: 'active' as const,
      details: `평점: ${gm.rating ?? '-'} / 리뷰: ${gm.reviewCount ?? '-'}`,
      // Use Maps URL from enrichment if available, fallback to search URL
      url: (gm as Record<string, unknown>).mapsUrl
        ? String((gm as Record<string, unknown>).mapsUrl)
        : gm.name ? `https://www.google.com/maps/search/${encodeURIComponent(String(gm.name))}` : '',
    };
    if (gmChannelIdx >= 0) {
      merged.otherChannels[gmChannelIdx] = gmChannel;
    } else {
      merged.otherChannels = [...merged.otherChannels, gmChannel];
    }
  }

  // 강남언니 enrichment
  if (enrichment.gangnamUnni) {
    const gu = enrichment.gangnamUnni;

    // Update clinic snapshot with real gangnamUnni data
    merged.clinicSnapshot = {
      ...merged.clinicSnapshot,
      overallRating: gu.rating ?? merged.clinicSnapshot.overallRating,
      totalReviews: gu.totalReviews ?? merged.clinicSnapshot.totalReviews,
      certifications: gu.badges?.length ? gu.badges : merged.clinicSnapshot.certifications,
      staffCount: gu.doctors?.length ?? merged.clinicSnapshot.staffCount, // 전문의 수 (강남언니 등록 의사 기준)
    };

    // Extract nearest station from address (Korean station name pattern)
    const addressToSearch = gu.address || merged.clinicSnapshot.location;
    if (addressToSearch && !merged.clinicSnapshot.nearestStation) {
      const stationMatch = addressToSearch.match(/(\S+역)/);
      if (stationMatch) {
        merged.clinicSnapshot = { ...merged.clinicSnapshot, nearestStation: stationMatch[1] };
      }
    }

    // Update lead doctor with gangnamUnni doctor data
    if (gu.doctors?.length) {
      const topDoctor = gu.doctors[0];
      if (topDoctor?.name) {
        merged.clinicSnapshot = {
          ...merged.clinicSnapshot,
          leadDoctor: {
            name: topDoctor.name,
            credentials: topDoctor.specialty || merged.clinicSnapshot.leadDoctor.credentials,
            rating: topDoctor.rating ?? 0,
            reviewCount: topDoctor.reviews ?? 0,
          },
        };
      }
    }

    // Update gangnamUnni channel in otherChannels
    const guChannelIdx = merged.otherChannels.findIndex(c => c.name === '강남언니');
    const guChannel = {
      name: '강남언니',
      status: 'active' as const,
      details: `평점: ${gu.rating ?? '-'}${gu.ratingScale || '/10'} / 리뷰: ${gu.totalReviews?.toLocaleString() ?? '-'}건`,
      url: gu.sourceUrl || '',
    };
    if (guChannelIdx >= 0) {
      merged.otherChannels[guChannelIdx] = guChannel;
    } else {
      merged.otherChannels = [...merged.otherChannels, guChannel];
    }
  }

  // Facebook enrichment
  if (enrichment.facebook) {
    const fb = enrichment.facebook;
    merged.facebookAudit = {
      ...merged.facebookAudit,
      pages: [{
        url: fb.pageUrl || '',
        pageName: fb.pageName || '',
        language: 'KR',
        label: '메인',
        followers: fb.followers ?? 0,
        following: 0,
        category: fb.categories?.join(', ') || '',
        bio: fb.intro || '',
        logo: '',
        logoDescription: '',
        link: fb.website || '',
        linkedDomain: fb.website || '',
        reviews: (() => {
          // Facebook rating 문자열 파싱: "Not yet rated (3 Reviews)" or "4.8 (120 Reviews)"
          const m = String(fb.rating || '').match(/\((\d+)\s+Reviews?\)/i);
          return m ? parseInt(m[1], 10) : 0;
        })(),
        recentPostAge: '',
        hasWhatsApp: false,
      }],
    };
  }

  // 네이버 블로그 enrichment
  if (enrichment.naverBlog) {
    const nb = enrichment.naverBlog;
    const nbChannelIdx = merged.otherChannels.findIndex(c => c.name === '네이버 블로그');
    const nbChannel = {
      name: '네이버 블로그',
      status: 'active' as const,
      details: `검색 결과: ${nb.totalResults?.toLocaleString() ?? '-'}건 / 최근 포스트 ${nb.posts?.length ?? 0}개`,
      // Prefer official blog URL from Phase 1, fallback to search URL
      url: (nb as Record<string, unknown>).officialBlogUrl
        ? String((nb as Record<string, unknown>).officialBlogUrl)
        : nb.searchQuery ? `https://search.naver.com/search.naver?where=blog&query=${encodeURIComponent(String(nb.searchQuery))}` : '',
    };
    if (nbChannelIdx >= 0) {
      merged.otherChannels[nbChannelIdx] = nbChannel;
    } else {
      merged.otherChannels = [...merged.otherChannels, nbChannel];
    }
  }

  // 네이버 플레이스 enrichment
  if (enrichment.naverPlace) {
    const np = enrichment.naverPlace;
    const npChannelIdx = merged.otherChannels.findIndex(c => c.name === '네이버 플레이스');
    const npChannel = {
      name: '네이버 플레이스',
      status: 'active' as const,
      details: np.category || '',
      // np.link is the clinic's own website, NOT Naver Place page
      // Use Naver Place search URL instead
      url: np.name ? `https://map.naver.com/v5/search/${encodeURIComponent(String(np.name))}` : '',
    };
    if (npChannelIdx >= 0) {
      merged.otherChannels[npChannelIdx] = npChannel;
    } else {
      merged.otherChannels = [...merged.otherChannels, npChannel];
    }

    // Update clinic phone/address from Naver Place if available
    if (np.telephone) {
      merged.clinicSnapshot = { ...merged.clinicSnapshot, phone: np.telephone };
    }
  }

  // Generate data-driven diagnosis from enrichment data
  const enrichDiagnosis = generateEnrichmentDiagnosis(enrichment);
  if (enrichDiagnosis.length > 0) {
    merged.problemDiagnosis = [...merged.problemDiagnosis, ...enrichDiagnosis];
  }

  // ── 스크린샷 영구 반영 ──────────────────────────────────────────────────────
  // channel_data.screenshots → report.screenshots 로 옮기고,
  // 채널별로 diagnosis evidenceIds 자동 연결
  if (enrichment.screenshots?.length) {
    const ss = enrichment.screenshots;

    // 1) report.screenshots 세팅 (ScreenshotEvidence 형식으로 변환)
    merged.screenshots = ss.map(s => ({
      id: s.id,
      url: s.url,
      channel: s.channel,
      caption: s.caption,
      capturedAt: s.capturedAt ?? new Date().toISOString(),
      sourceUrl: s.sourceUrl,
    }));

    // 2) 채널명 → screenshot IDs 매핑 테이블 생성
    //    channel_data의 channel 필드: "YouTube", "웹사이트", "Instagram", "Facebook" 등
    const CHANNEL_ALIAS: Record<string, string[]> = {
      youtube: ['youtube', 'YouTube', 'yt'],
      instagram: ['instagram', 'Instagram', 'ig'],
      facebook: ['facebook', 'Facebook', 'fb'],
      website: ['웹사이트', 'website', 'Website'],
      gangnamUnni: ['강남언니', 'gangnamUnni'],
      naverPlace: ['네이버 플레이스', 'naverPlace'],
      naverBlog: ['네이버 블로그', 'naverBlog'],
    };

    const channelToIds: Record<string, string[]> = {};
    for (const s of ss) {
      for (const [key, aliases] of Object.entries(CHANNEL_ALIAS)) {
        if (aliases.some(a => s.channel.toLowerCase().includes(a.toLowerCase()))) {
          channelToIds[key] = [...(channelToIds[key] ?? []), s.id];
          break;
        }
      }
    }

    // 3) 채널별 audit.diagnosis 배열에 evidenceIds 연결
    //    YouTubeAudit / InstagramAudit / FacebookAudit 컴포넌트가 이 필드를 사용함
    const linkIds = (diagItems: import('../types/report').DiagnosisItem[], channelKey: string): import('../types/report').DiagnosisItem[] => {
      const ids = channelToIds[channelKey] ?? [];
      if (!ids.length) return diagItems;
      return diagItems.map(item => ({ ...item, evidenceIds: [...(item.evidenceIds ?? []), ...ids] }));
    };

    if (merged.youtubeAudit?.diagnosis?.length) {
      merged.youtubeAudit = { ...merged.youtubeAudit, diagnosis: linkIds(merged.youtubeAudit.diagnosis, 'youtube') };
    }
    if (merged.instagramAudit?.diagnosis?.length) {
      merged.instagramAudit = { ...merged.instagramAudit, diagnosis: linkIds(merged.instagramAudit.diagnosis, 'instagram') };
    }
    if (merged.facebookAudit?.diagnosis?.length) {
      merged.facebookAudit = { ...merged.facebookAudit, diagnosis: linkIds(merged.facebookAudit.diagnosis, 'facebook') };
    }
    // websiteAudit / 기타 채널은 EvidenceGallery를 직접 받지 않으므로 problemDiagnosis에만 연결
    merged.problemDiagnosis = merged.problemDiagnosis.map(item => {
      const catLower = item.category.toLowerCase();
      let ids: string[] = [];
      for (const [key, ssIds] of Object.entries(channelToIds)) {
        if (catLower.includes(key) || key.includes(catLower)) {
          ids = [...ids, ...ssIds];
        }
      }
      return ids.length > 0 ? { ...item, evidenceIds: ids } : item;
    });
  }
  // ───────────────────────────────────────────────────────────────────────────

  return merged;
}
