/*
 * 엔드포인트 래퍼 — 계약(§2~§6) 경로를 타입 안전하게 노출.
 * 501(NOT_IMPLEMENTED)인 스켈레톤 경로도 포함(호출부에서 code로 degraded 처리).
 */
import { api } from './client';
import type {
  AcceptInviteRequest,
  AdminDashboardData,
  AdminStats,
  AnalyticsData,
  AnalyticsPeriod,
  AnalyticsPerspective,
  AutoLayoutOption,
  AutoLayoutRequest,
  BoothDto,
  ComplianceReport,
  HallTreeDto,
  S7PreviewResult,
  DashboardData,
  OpsData,
  DesignPlanDto,
  DesignSaveRequest,
  DesignSpec,
  ExhibitionDto,
  ForgotPasswordRequest,
  KintexPrincipal,
  LayoutDto,
  LayoutInterpretRequest,
  LayoutInterpretResult,
  LayoutSaveRequest,
  LoginRequest,
  LoginResponse,
  LoginSlideDto,
  LoginSlideUpdateRequest,
  MeetingActionDto,
  MeetingActionRequest,
  MeetingDetail,
  MeetingDto,
  MeetingSaveRequest,
  MessageDto,
  MessageSendRequest,
  NoticeDto,
  NoticeSaveRequest,
  NotificationDto,
  OpinionCommentDto,
  OpinionDetail,
  OpinionDto,
  OpinionSaveRequest,
  OtpSetupResponse,
  OtpStatusResponse,
  PageResponse,
  RegisterRequest,
  RegisterResponse,
  RenderJobDto,
  RenderJobRequest,
  ReportDto,
  ReportSaveRequest,
  ResetPasswordRequest,
  ScheduleDto,
  ScheduleSaveRequest,
  SearchResultItem,
  SecureLoginResponse,
  UtilityOrderDto,
  UtilityQuote,
  UtilityQuoteRequest,
  WiringRequest,
  WiringResult,
  WorkStatsDto,
  WorklogDto,
  WorklogSaveRequest,
  WorkspaceDto,
} from './types';

// ── 인증·워크스페이스 (SCR-01) ──
export const authApi = {
  // 레거시 단일 단계 로그인(하위호환 유지). 신규 UI 는 secureLogin 사용.
  login: (body: LoginRequest) =>
    api.post<LoginResponse>('/api/auth/login', body, { anonymous: true }),
  // 2FA/잠금 적용 1단계 — status(OK|OTP_REQUIRED|OTP_ENROLL) 분기.
  secureLogin: (body: LoginRequest) =>
    api.post<SecureLoginResponse>('/api/auth/login/secure', body, { anonymous: true }),
  // 2단계(등록 사용자) — challengeToken + 6자리 → 최종 토큰.
  otpVerify: (challengeToken: string, code: string) =>
    api.post<LoginResponse>('/api/auth/otp/verify', { challengeToken, code }, { anonymous: true }),
  // 등록 강제 setup — challengeToken 으로 시크릿·QR 발급(프리오스).
  otpEnrollSetup: (challengeToken: string) =>
    api.post<OtpSetupResponse>('/api/auth/otp/enroll/setup', { challengeToken }, { anonymous: true }),
  // 등록 강제 verify — 코드 확인·활성화 → 최종 토큰.
  otpEnrollVerify: (challengeToken: string, code: string) =>
    api.post<LoginResponse>('/api/auth/otp/enroll/verify', { challengeToken, code }, { anonymous: true }),
  // 마이페이지(인증) — 본인 OTP 등록/확인/상태/해제.
  otpSetup: () => api.post<OtpSetupResponse>('/api/auth/otp/setup'),
  otpConfirm: (code: string) => api.post<void>('/api/auth/otp/confirm', { code }),
  otpStatus: () => api.get<OtpStatusResponse>('/api/auth/otp/status'),
  otpResetSelf: () => api.post<void>('/api/auth/otp/reset'),
  workspaces: () => api.get<WorkspaceDto[]>('/api/auth/workspaces'),
  me: () => api.get<KintexPrincipal>('/api/auth/me'),
  acceptInvite: (body: AcceptInviteRequest) =>
    api.post<LoginResponse>('/api/auth/accept-invite', body),
  // 회원가입 — 성공 시 상태(승인 대기/활성) 반환. 스켈레톤이면 501(호출부 degraded).
  register: (body: RegisterRequest) =>
    api.post<RegisterResponse>('/api/auth/register', body, { anonymous: true }),
  // 비밀번호 찾기(요청) — 사용자 열거 방지 위해 백엔드는 항상 성공 처리.
  forgotPassword: (body: ForgotPasswordRequest) =>
    api.post<void>('/api/auth/password/forgot', body, { anonymous: true }),
  // 비밀번호 재설정 — 이메일+인증코드+새 비밀번호.
  resetPassword: (body: ResetPasswordRequest) =>
    api.post<void>('/api/auth/password/reset', body, { anonymous: true }),
};

// ── 전시 일정 카탈로그 (SCR-15) ──
// 제안 계약(백엔드 갭): `GET /api/events` → ExhibitionDto[] (전 홀 10년치 카탈로그).
// 인증 workspaces(내가 멤버인 행사)와 달리 이 경로는 전체 행사 카탈로그를 제공한다.
// 백엔드 미구현 시 NOT_IMPLEMENTED(501)/NOT_FOUND(404) → 호출부에서 크롤링 샘플 폴백.
export const exhibitionApi = {
  list: (params?: { year?: string; status?: string }) => {
    const qs = new URLSearchParams();
    if (params?.year && params.year !== 'all') qs.set('year', params.year);
    if (params?.status && params.status !== 'all') qs.set('status', params.status);
    const q = qs.toString();
    return api.get<ExhibitionDto[]>(`/api/events${q ? `?${q}` : ''}`);
  },
};

// ── 시스템관리 — 사용자(관리자) ──
// 사용자 관리 화면은 후속(현재 API 계약만). OTP 초기화는 SystemAccessGuard.requireAdmin + @Audited.
export const adminUserApi = {
  // 관리자 — 대상 사용자 OTP 강제 초기화(분실/기기 교체 대응). 다음 로그인 시 재등록.
  otpReset: (userId: string) =>
    api.post<void>(`/api/admin/users/${encodeURIComponent(userId)}/otp/reset`),
};

// ── 로그인 슬라이드 (SCR-01 캐러셀 · 관리자) ──
export const loginSlideApi = {
  // 공개 — 활성만, 최신 등록순 최대 5건(인증 불필요).
  publicList: () =>
    api.get<LoginSlideDto[]>('/api/public/login-slides', { anonymous: true }),
  // 관리자 — 전체 목록(비활성 포함, 최신 등록순).
  adminList: () => api.get<LoginSlideDto[]>('/api/admin/login-slides'),
  // 관리자 — 신규 등록(이미지 multipart + 문구).
  create: (image: File, title: string, caption: string) => {
    const form = new FormData();
    form.append('image', image);
    form.append('title', title);
    if (caption) form.append('caption', caption);
    return api.postForm<LoginSlideDto>('/api/admin/login-slides', form);
  },
  // 관리자 — 문구·정렬·활성 여부 수정.
  update: (id: number, body: LoginSlideUpdateRequest) =>
    api.put<LoginSlideDto>(`/api/admin/login-slides/${id}`, body),
  // 관리자 — 삭제.
  remove: (id: number) => api.del<void>(`/api/admin/login-slides/${id}`),
};

// ── M2 플로어플랜 (SCR-03/04) ──
const layoutBase = (eventId: string, hallId: string) =>
  `/api/events/${encodeURIComponent(eventId)}/halls/${encodeURIComponent(hallId)}/layout`;

export const layoutApi = {
  get: (eventId: string, hallId: string, version?: number) =>
    api.get<LayoutDto>(
      `${layoutBase(eventId, hallId)}${version != null ? `?version=${version}` : ''}`,
    ),
  save: (eventId: string, hallId: string, body: LayoutSaveRequest) =>
    api.put<LayoutDto>(layoutBase(eventId, hallId), body),
  validate: (eventId: string, hallId: string, version?: number) =>
    api.post<ComplianceReport>(
      `${layoutBase(eventId, hallId)}/validate${version != null ? `?version=${version}` : ''}`,
    ),
  // 자연어 조건 해석 — 문장 → AutoLayoutRequest(서버 클램핑). degraded 시 프론트 수동 폼 폴백.
  interpret: (eventId: string, hallId: string, body: LayoutInterpretRequest) =>
    api.post<LayoutInterpretResult>(
      `${layoutBase(eventId, hallId)}/auto-generate/interpret`,
      body,
    ),
  autoGenerate: (eventId: string, hallId: string, body: AutoLayoutRequest, ai = false) =>
    api.post<AutoLayoutOption[]>(
      `${layoutBase(eventId, hallId)}/auto-generate${ai ? '?ai=true' : ''}`,
      body,
    ),
  // "이 안으로 편집 시작" 실저장 — 선택 안 부스를 새 배치안 버전으로 영속(규정 재검증 포함).
  applyOption: (eventId: string, hallId: string, body: { name: string; booths: BoothDto[] }) =>
    api.post<LayoutDto>(`${layoutBase(eventId, hallId)}/apply-option`, body),
  // S7 홀 전경 이미지 생성 — 버튼 클릭 시에만 발행(자동 발행 금지, 2026-07-14).
  previewS7: (eventId: string, hallId: string, option?: string) =>
    api.post<S7PreviewResult>(
      `${layoutBase(eventId, hallId)}/auto-generate/preview${
        option ? `?option=${encodeURIComponent(option)}` : ''
      }`,
    ),
};

// 평면도 선택 트리 — 대분류(전시장) > 중분류(홀) > 소분류(구역) (V60)
export const hallTreeApi = {
  get: () => api.get<HallTreeDto>('/api/halls/tree'),
};

// ── M3 부스 설계 스튜디오 (SCR-06/09) ──
const designBase = (eventId: string, boothId: string) =>
  `/api/events/${encodeURIComponent(eventId)}/booths/${encodeURIComponent(boothId)}/design`;

export const designApi = {
  get: (eventId: string, boothId: string, version?: number) =>
    api.get<DesignPlanDto>(
      `${designBase(eventId, boothId)}${version != null ? `?version=${version}` : ''}`,
    ),
  save: (eventId: string, boothId: string, body: DesignSaveRequest) =>
    api.put<DesignPlanDto>(designBase(eventId, boothId), body),
  // precheck는 스펙을 직접 받아 즉시 룰 평가(계약 §4 — 완성 엔드포인트).
  precheck: (eventId: string, boothId: string, spec: DesignSpec) =>
    api.post<ComplianceReport>(`${designBase(eventId, boothId)}/precheck`, spec),
};

// ── M4 유틸리티 (SCR-07/08) ──
const utilityBase = (eventId: string, boothId: string) =>
  `/api/events/${encodeURIComponent(eventId)}/booths/${encodeURIComponent(boothId)}/utility`;

export const utilityApi = {
  quote: (eventId: string, boothId: string, body: UtilityQuoteRequest) =>
    api.post<UtilityQuote>(`${utilityBase(eventId, boothId)}/quote`, body),
  wiring: (eventId: string, boothId: string, hallId: string, body: WiringRequest) =>
    api.post<WiringResult>(
      `${utilityBase(eventId, boothId)}/wiring?hallId=${encodeURIComponent(hallId)}`,
      body,
    ),
  order: (eventId: string, boothId: string, body: UtilityQuoteRequest) =>
    api.post<UtilityOrderDto>(`${utilityBase(eventId, boothId)}/order`, body),
  get: (eventId: string, boothId: string) =>
    api.get<UtilityOrderDto>(utilityBase(eventId, boothId)),
};

// ── M5 RenderJob (SCR-06/12) ──
export const renderApi = {
  status: (eventId: string, jobId: string) =>
    api.get<RenderJobDto>(
      `/api/events/${encodeURIComponent(eventId)}/render-jobs/${encodeURIComponent(jobId)}`,
    ),
  // 갤러리 목록 (SCR-12) — shot 필터 선택.
  list: (eventId: string, boothId: string, shot?: string) =>
    api.get<RenderJobDto[]>(
      `/api/events/${encodeURIComponent(eventId)}/booths/${encodeURIComponent(boothId)}/render-jobs${
        shot ? `?shot=${encodeURIComponent(shot)}` : ''
      }`,
    ),
  // 발행 (SCR-06 예상 사진 생성) — 큐잉.
  create: (eventId: string, boothId: string, body: RenderJobRequest) =>
    api.post<RenderJobDto>(
      `/api/events/${encodeURIComponent(eventId)}/booths/${encodeURIComponent(boothId)}/render`,
      body,
    ),
};

/* ─────────────────────────────────────────────────────────
 * 대시보드 계열 집계 (SCR-02·13·14·16) — 실 API
 * 근거: 백엔드 com.zioinfo.kintex.{dashboard,analytics,admin,ops} 컨트롤러(정본).
 * 실패(NETWORK/NOT_FOUND) 시 각 화면이 샘플 폴백으로 강등.
 * ───────────────────────────────────────────────────────── */

// ── SCR-02 주최자 대시보드 집계 ──
export const dashboardApi = {
  get: (eventId: string) =>
    api.get<DashboardData>(`/api/events/${encodeURIComponent(eventId)}/dashboard`),
};

// ── SCR-13 경영분석 BI 집계 ──
export const analyticsApi = {
  get: (eventId: string, perspective: AnalyticsPerspective, period: AnalyticsPeriod) =>
    api.get<AnalyticsData>(
      `/api/events/${encodeURIComponent(eventId)}/analytics?perspective=${perspective}&period=${period}`,
    ),
};

// ── SCR-14 관리자 백오피스 대시보드 ──
export const adminApi = {
  dashboard: (tenant?: string) =>
    api.get<AdminDashboardData>(
      `/api/admin/dashboard${tenant ? `?tenant=${encodeURIComponent(tenant)}` : ''}`,
    ),
  // SCR-16 실 집계 지표(사용자·행사·테넌트·등록업체·오늘 체크인) — 실 테이블 존재분만.
  stats: (tenant?: string) =>
    api.get<AdminStats>(
      `/api/admin/stats${tenant ? `?tenant=${encodeURIComponent(tenant)}` : ''}`,
    ),
};

// ── SCR-16 홀 현장 운영 ──
export const opsApi = {
  get: (eventId: string) =>
    api.get<OpsData>(`/api/events/${encodeURIComponent(eventId)}/ops`),
};

/* ─────────────────────────────────────────────────────────
 * §5B 공통 업무 기능 (SCR-39~48) — /api/work/*
 * 근거: 백엔드 com.zioinfo.kintex.work.* 컨트롤러 시그니처(정본).
 * ───────────────────────────────────────────────────────── */

function qs(params: Record<string, string | number | boolean | undefined | null>): string {
  const sp = new URLSearchParams();
  for (const [k, v] of Object.entries(params)) {
    if (v !== undefined && v !== null && v !== '') sp.set(k, String(v));
  }
  const s = sp.toString();
  return s ? `?${s}` : '';
}

// ── SCR-39 업무일지 ──
export interface WorklogListParams {
  eventId?: string;
  fromDate?: string;
  toDate?: string;
  status?: string;
  page?: number;
  size?: number;
}
export const worklogApi = {
  list: (p: WorklogListParams = {}) =>
    api.get<PageResponse<WorklogDto>>(`/api/work/worklogs${qs({ ...p })}`),
  get: (id: string) => api.get<WorklogDto>(`/api/work/worklogs/${encodeURIComponent(id)}`),
  create: (body: WorklogSaveRequest) => api.post<WorklogDto>('/api/work/worklogs', body),
  update: (id: string, body: WorklogSaveRequest) =>
    api.put<WorklogDto>(`/api/work/worklogs/${encodeURIComponent(id)}`, body),
  remove: (id: string) => api.del<void>(`/api/work/worklogs/${encodeURIComponent(id)}`),
};

// ── SCR-40 통합 일정 ──
export const scheduleWorkApi = {
  list: (p: { eventId?: string; fromAt?: string; toAt?: string } = {}) =>
    api.get<ScheduleDto[]>(`/api/work/schedules${qs({ ...p })}`),
  get: (id: string) => api.get<ScheduleDto>(`/api/work/schedules/${encodeURIComponent(id)}`),
  create: (body: ScheduleSaveRequest) => api.post<ScheduleDto>('/api/work/schedules', body),
  update: (id: string, body: ScheduleSaveRequest) =>
    api.put<ScheduleDto>(`/api/work/schedules/${encodeURIComponent(id)}`, body),
  remove: (id: string) => api.del<void>(`/api/work/schedules/${encodeURIComponent(id)}`),
};

// ── SCR-41 쪽지 ──
export const messageApi = {
  inbox: (page = 0, size = 20) =>
    api.get<PageResponse<MessageDto>>(`/api/work/messages/inbox${qs({ page, size })}`),
  sent: (page = 0, size = 20) =>
    api.get<PageResponse<MessageDto>>(`/api/work/messages/sent${qs({ page, size })}`),
  unreadCount: () => api.get<{ unread: number }>('/api/work/messages/unread-count'),
  send: (body: MessageSendRequest) => api.post<{ id: string }>('/api/work/messages', body),
  read: (id: string) => api.post<void>(`/api/work/messages/${encodeURIComponent(id)}/read`),
  remove: (id: string) => api.del<void>(`/api/work/messages/${encodeURIComponent(id)}`),
};

// ── SCR-42 공지 (조회=인증, 쓰기=매니저) ──
export const noticeApi = {
  list: (p: { eventId?: string; category?: string; keyword?: string; page?: number; size?: number } = {}) =>
    api.get<PageResponse<NoticeDto>>(`/api/work/notices${qs({ ...p })}`),
  get: (id: string) => api.get<NoticeDto>(`/api/work/notices/${encodeURIComponent(id)}`),
  create: (body: NoticeSaveRequest) => api.post<NoticeDto>('/api/work/notices', body),
  update: (id: string, body: NoticeSaveRequest) =>
    api.put<NoticeDto>(`/api/work/notices/${encodeURIComponent(id)}`, body),
  remove: (id: string) => api.del<void>(`/api/work/notices/${encodeURIComponent(id)}`),
};

// ── SCR-43 의견접수 (조회/등록=인증, 상태·답변=매니저) ──
export const opinionApi = {
  list: (p: { eventId?: string; status?: string; page?: number; size?: number } = {}) =>
    api.get<PageResponse<OpinionDto>>(`/api/work/opinions${qs({ ...p })}`),
  get: (id: string) => api.get<OpinionDetail>(`/api/work/opinions/${encodeURIComponent(id)}`),
  create: (body: OpinionSaveRequest) => api.post<OpinionDto>('/api/work/opinions', body),
  changeStatus: (id: string, value: string) =>
    api.patch<void>(`/api/work/opinions/${encodeURIComponent(id)}/status${qs({ value })}`),
  comment: (id: string, content: string) =>
    api.post<OpinionCommentDto>(`/api/work/opinions/${encodeURIComponent(id)}/comments`, { content }),
  remove: (id: string) => api.del<void>(`/api/work/opinions/${encodeURIComponent(id)}`),
};

// ── SCR-44 통합검색 ──
export const searchApi = {
  search: (q: string, limit = 30) =>
    api.get<SearchResultItem[]>(`/api/work/search${qs({ q, limit })}`),
};

// ── SCR-45 회의록 ──
export const meetingApi = {
  list: (p: { eventId?: string; page?: number; size?: number } = {}) =>
    api.get<PageResponse<MeetingDto>>(`/api/work/meetings${qs({ ...p })}`),
  get: (id: string) => api.get<MeetingDetail>(`/api/work/meetings/${encodeURIComponent(id)}`),
  create: (body: MeetingSaveRequest) => api.post<MeetingDto>('/api/work/meetings', body),
  update: (id: string, body: MeetingSaveRequest) =>
    api.put<MeetingDto>(`/api/work/meetings/${encodeURIComponent(id)}`, body),
  saveMinutes: (id: string, minutes: string) =>
    api.put<void>(`/api/work/meetings/${encodeURIComponent(id)}/minutes`, { minutes }),
  addAction: (id: string, body: MeetingActionRequest) =>
    api.post<MeetingActionDto>(`/api/work/meetings/${encodeURIComponent(id)}/actions`, body),
  actionStatus: (actionId: string, value: string) =>
    api.patch<void>(`/api/work/meetings/actions/${encodeURIComponent(actionId)}/status${qs({ value })}`),
  remove: (id: string) => api.del<void>(`/api/work/meetings/${encodeURIComponent(id)}`),
};

// ── SCR-46 업무보고 + 통계 ──
export const reportApi = {
  list: (p: { eventId?: string; reportType?: string; page?: number; size?: number } = {}) =>
    api.get<PageResponse<ReportDto>>(`/api/work/reports${qs({ ...p })}`),
  get: (id: string) => api.get<ReportDto>(`/api/work/reports/${encodeURIComponent(id)}`),
  create: (body: ReportSaveRequest) => api.post<ReportDto>('/api/work/reports', body),
  update: (id: string, body: ReportSaveRequest) =>
    api.put<ReportDto>(`/api/work/reports/${encodeURIComponent(id)}`, body),
  remove: (id: string) => api.del<void>(`/api/work/reports/${encodeURIComponent(id)}`),
};
export const workStatsApi = {
  worklog: (p: { eventId?: string; fromDate?: string; toDate?: string } = {}) =>
    api.get<WorkStatsDto>(`/api/work/stats/worklog${qs({ ...p })}`),
};

// ── SCR-47 알림센터 ──
export const notificationApi = {
  list: (p: { unreadOnly?: boolean; page?: number; size?: number } = {}) =>
    api.get<PageResponse<NotificationDto>>(`/api/work/notifications${qs({ ...p })}`),
  unreadCount: () => api.get<{ unread: number }>('/api/work/notifications/unread-count'),
  read: (id: string) => api.post<void>(`/api/work/notifications/${encodeURIComponent(id)}/read`),
  readAll: () => api.post<void>('/api/work/notifications/read-all'),
  remove: (id: string) => api.del<void>(`/api/work/notifications/${encodeURIComponent(id)}`),
};
