/*
 * M10 관람객·리드 실 API 계약 (SCR-30·SCR-32).
 * 공용 클라이언트(../../api/client)만 사용 — client.ts·endpoints.ts·types.ts 무수정.
 * ★ PII 불변: 응답에는 마스킹 필드(nameMasked·phoneMasked·emailMasked)만 존재한다(원문 없음).
 *
 * 백엔드 컨트롤러 정본:
 *   GET  /api/events/{eventId}/visitors/summary → ApiResponse<VisitorSummary>
 *   GET  /api/events/{eventId}/visitors?page&size → ApiResponse<PageResponse<VisitorListItem>>
 *   GET  /api/events/{eventId}/leads?boothId&page&size → ApiResponse<PageResponse<LeadItem>>
 *   POST /api/public/events/{eventId}/register (공개 — 공개 P4 팀 배선)
 */
import { api, ApiRequestError, getAccessToken } from '../../api/client';
import type { PageResponse } from '../../api/types';

const API_BASE = (import.meta.env.VITE_API_BASE as string | undefined) ?? '';

// ── SCR-30 요약 ──
export interface VisitorKpiDto {
  label: string;
  value: string;
  delta?: string | null;
  trend?: string | null;
  ai?: boolean | null;
  sub?: string | null;
}
export interface TrendPointDto {
  label: string;
  preReg: number;
  checkIn: number;
}
export interface TypeSliceDto {
  name: string;
  value: number;
  color: string;
}
export interface FormItemDto {
  id: string;
  name: string;
  active: boolean;
  count: number;
}
export interface VisitorSummaryDto {
  kpis: VisitorKpiDto[];
  trend: TrendPointDto[];
  types: TypeSliceDto[];
  forms: FormItemDto[];
}

// ── SCR-30 등록자(마스킹) ──
export interface VisitorListItemDto {
  id: string;
  nameMasked: string;
  type: string; // visitor|buyer|vip
  company: string | null;
  registeredAt: string | null;
  checkin: string; // done|waiting|cancelled
  badgeIssued: boolean;
}

// ── SCR-32 리드(마스킹 + AI 저장값) ──
export interface LeadActivityDto {
  text: string;
  at: string;
}
export interface LeadItemDto {
  id: string;
  nameMasked: string;
  role: string | null;
  company: string | null;
  product: string | null;
  interest: number;
  score: number;
  collectedAt: string | null;
  phoneMasked: string | null;
  emailMasked: string | null;
  aiReasons: string[];
  activity: LeadActivityDto[];
  followupDraft: string | null;
  aiGenerated: boolean;
}

// ── SCR-32 리드 AI 재계산(F051) ──
export interface LeadRescoreResultDto {
  leadId: string;
  score: number;
  aiReasons: string[];
  scoreSource: string; // 'ai' | 'rule'
  aiGenerated: boolean;
  degraded: boolean;
  provider: string; // 'claude' | 'ollama' | 'none'
}

// ── SCR-31 현장 체크인 ──
export interface CheckinResultDto {
  registrationId: string;
  nameMasked: string;
  type: string; // visitor|buyer|vip
  company: string | null;
  badgeCode: string | null;
  badgeIssued: boolean;
  checkinState: string; // done|waiting|cancelled
  alreadyCheckedIn: boolean;
  checkinAt: string | null;
}
export interface CheckinSearchItemDto {
  registrationId: string;
  nameMasked: string;
  type: string;
  company: string | null;
  phoneMasked: string | null;
  badgeCode: string | null;
  checkinState: string;
}
export interface HourBucketDto {
  hour: string;
  count: number;
}
export interface CheckinStatsDto {
  preRegistered: number;
  checkedIn: number;
  inside: number;
  byHour: HourBucketDto[];
}
export interface BadgeReissueResultDto {
  registrationId: string;
  badgeCode: string;
  badgeIssued: boolean;
}

function evPath(eventId: string): string {
  return `/api/events/${encodeURIComponent(eventId)}`;
}

export const visitorApi = {
  summary: (eventId: string) =>
    api.get<VisitorSummaryDto>(`${evPath(eventId)}/visitors/summary`),
  visitors: (eventId: string, page = 0, size = 20) =>
    api.get<PageResponse<VisitorListItemDto>>(`${evPath(eventId)}/visitors?page=${page}&size=${size}`),
  leads: (eventId: string, boothId?: string | null, page = 0, size = 50) => {
    const qs = new URLSearchParams();
    if (boothId) qs.set('boothId', boothId);
    qs.set('page', String(page));
    qs.set('size', String(size));
    return api.get<PageResponse<LeadItemDto>>(`${evPath(eventId)}/leads?${qs.toString()}`);
  },
  /** 리드 AI 재계산(F051, 매니저↑) — AI 실패 시 서버가 기존 규칙 스코어 유지(scoreSource='rule'·degraded=true). */
  rescoreLead: (eventId: string, leadId: string) =>
    api.post<LeadRescoreResultDto>(
      `${evPath(eventId)}/leads/${encodeURIComponent(leadId)}/rescore`,
    ),

  // SCR-31 체크인 데스크 — 토큰/등록번호 체크인, 수동 검색, 실시간 집계, 배지 재발급.
  checkin: (eventId: string, body: { token?: string; registrationId?: string }) =>
    api.post<CheckinResultDto>(`${evPath(eventId)}/checkin`, body),
  checkinSearch: (eventId: string, q: string) =>
    api.get<CheckinSearchItemDto[]>(`${evPath(eventId)}/checkin/search?q=${encodeURIComponent(q)}`),
  checkinStats: (eventId: string) =>
    api.get<CheckinStatsDto>(`${evPath(eventId)}/checkin/stats`),
  reissueBadge: (eventId: string, registrationId: string) =>
    api.post<BadgeReissueResultDto>(
      `${evPath(eventId)}/visitors/${encodeURIComponent(registrationId)}/badge/reissue`,
    ),

  // SCR-32 리드 CSV 내보내기 — 봉투가 아닌 파일(byte) 응답이라 fetch 직결 후 blob 다운로드.
  exportLeadsCsv: async (eventId: string, boothId?: string | null): Promise<void> => {
    const qs = boothId ? `?boothId=${encodeURIComponent(boothId)}` : '';
    const token = getAccessToken();
    let res: Response;
    try {
      res = await fetch(`${API_BASE}${evPath(eventId)}/leads/export${qs}`, {
        headers: token ? { Authorization: `Bearer ${token}` } : {},
      });
    } catch {
      throw new ApiRequestError('NETWORK', '네트워크 연결을 확인해 주세요.', 0);
    }
    if (!res.ok) {
      throw new ApiRequestError('UNKNOWN', `CSV 내보내기에 실패했습니다. (${res.status})`, res.status);
    }
    const blob = await res.blob();
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `leads-${eventId}.csv`;
    document.body.appendChild(a);
    a.click();
    a.remove();
    URL.revokeObjectURL(url);
  },
};
