/*
 * 백엔드 API 계약 타입 (단일 출처: _workspace/01_backend_contracts.md)
 * ★ 이 타입들은 백엔드 응답 shape과 정확히 일치해야 한다. 불일치 발견 시 _workspace/에 기록·보고.
 */

// ── 0-1 응답 봉투 ──
export interface ApiError {
  code: ApiErrorCode;
  message: string;
}
export interface ApiResponse<T> {
  success: boolean;
  data: T | null;
  error: ApiError | null;
}
export interface PageResponse<T> {
  items: T[];
  page: number;
  size: number;
  total: number;
}

// ── 0-2 오류 코드 ──
export type ApiErrorCode =
  | 'VALIDATION'
  | 'UNAUTHORIZED'
  | 'FORBIDDEN'
  | 'NOT_FOUND'
  | 'CONFLICT'
  | 'COMPLIANCE_BLOCKED'
  | 'RENDER_QUOTA_EXCEEDED'
  | 'NOT_REGISTERED_COMPANY'
  | 'NOT_IMPLEMENTED'
  | 'ACCOUNT_LOCKED'
  | 'OTP_REQUIRED'
  | 'OTP_INVALID'
  | 'INTERNAL';

// ── 0-5 역할 ──
export type EventRole = 'ORGANIZER' | 'EXHIBITOR' | 'CONTRACTOR' | 'HALL_MANAGER';

// ── 2. 인증·워크스페이스 (SCR-01) ──
export interface WorkspaceDto {
  eventId: string;
  eventName: string;
  startDate: string;
  endDate: string;
  hallLabel: string;
  myRole: EventRole;
  dday: number;
}
// ── 전시 일정 카탈로그 (SCR-15 · M1·M6) ──
// 원천: `event` 테이블(V10 seed_exhibitions_10yr — kintex.com 크롤링 10년치 1,098건).
// 제안 계약: `GET /api/events` → ExhibitionDto[] (백엔드 갭 06_backend_api_gaps.md §1).
// DB 컬럼(id·name·start_date·end_date·status)은 필수, 나머지는 후속 enrich(nullable).
export type ExhibitionStatus = 'ongoing' | 'upcoming' | 'ended';
export interface ExhibitionDto {
  id: string;
  name: string;
  startDate: string; // "2026-08-11"
  endDate: string; // "2026-08-14"
  status: ExhibitionStatus | string; // DB 원문(ended 등) — 프론트는 날짜 기준 재계산
  hallLabel?: string | null; // 배정 홀(후속 enrich) — 예 "제2전시장 홀7"
  category?: string | null; // 카테고리(후속 enrich)
  estVisitors?: number | null; // 추정 인원(후속 enrich)
}

export interface AuthUser {
  userId: string;
  displayName: string;
  hallManager: boolean;
  // 전역 역할(app_user.role_code — 'ADMIN' | 'MANAGER' | …). 관람객 등 미지정 계정은 null/undefined.
  roleCode?: string | null;
}
export interface LoginResponse {
  accessToken: string;
  expiresInSeconds: number;
  user: AuthUser;
  workspaces: WorkspaceDto[];
}
export interface LoginRequest {
  email: string;
  password: string;
}

// ── 2차 인증(OTP·TOTP) — 로그인 2단계 + 등록 강제 ──
// 1단계 /api/auth/login/secure 응답 status:
//  - OK           → OTP 미사용(관람객). login 에 최종 토큰.
//  - OTP_REQUIRED → 등록 사용자. challengeToken 으로 /otp/verify(6자리) → 최종 토큰.
//  - OTP_ENROLL   → 필수 역할 미등록. challengeToken 으로 /otp/enroll/setup → /otp/enroll/verify.
export type SecureLoginStatus = 'OK' | 'OTP_REQUIRED' | 'OTP_ENROLL';
export interface SecureLoginResponse {
  status: SecureLoginStatus;
  challengeToken: string | null;
  login: LoginResponse | null;
}
// 시크릿·otpauth URI·QR(data:image/png;base64) — 등록 화면 1회 표기용(시크릿은 저장/재노출 금지).
export interface OtpSetupResponse {
  secret: string;
  otpAuthUri: string;
  qrImageDataUri: string;
}
export interface OtpStatusResponse {
  otpEnabled: boolean;
  verifyMethod: string;
}
export interface AcceptInviteRequest {
  inviteCode: string;
  companyRegistrationNo: string;
}

// 회원가입 (SCR-01 Signup)
export interface RegisterRequest {
  email: string;
  displayName: string;
  password: string;
  companyName?: string;
  inviteCode?: string;
}
export interface RegisterResponse {
  userId: string;
  // 계정 상태 — 'ACTIVE'(즉시 로그인) | 'PENDING_APPROVAL'(승인 대기) 등 백엔드 권위
  status: string;
  message?: string;
}

// 비밀번호 찾기/초기화 (SCR-01 ForgotPassword 2단계)
export interface ForgotPasswordRequest {
  email: string;
}
export interface ResetPasswordRequest {
  email: string;
  code: string;
  newPassword: string;
}
export interface KintexPrincipal {
  userId: string;
  displayName: string;
  eventRoles: Record<string, EventRole>;
  hallManager: boolean;
  // 전역 역할(app_user.role_code, JWT rc 클레임 복원). 미지정 시 null/undefined.
  roleCode?: string | null;
}

// ── 3. M2 플로어플랜 (SCR-03/04) ──
export type BoothType = 'basic' | 'premium' | 'independent' | 'island';
export interface BoothDto {
  boothId: string;
  boothNo: string;
  type: BoothType | string;
  polygon: number[][]; // 홀 로컬 좌표계(미터), 닫힌 링
  sizeM: [number, number];
  heightM: number;
  floorLoadTPerM2: number;
  assignedCompanyName: string | null;
  premium: boolean;
}
export interface LayoutSummary {
  boothCount: number;
  targetBoothCount: number;
  salesAreaM2: number;
  minAisleWidthM: number;
  violationBlock: number;
  violationWarn: number;
}
/** 홀 기둥(V62 실측) — 자동배치 회피·캔버스 표시. 좌표는 홀 로컬 m(도면 상단 y=0). */
export interface HallPillarDto {
  pillarId: string;
  x: number;
  y: number;
  radiusM: number;
}
/** 트렌치 공급 포인트(V61/V65 실측 또는 가정 그리드) — 캔버스 표시·M4 배선 근거. */
export interface HallTrenchDto {
  trenchId: string;
  x: number;
  y: number;
  assumed: boolean;
  supplies: string[]; // power | water | air | network | gas
}
/** 벽·계단·엘리베이터·문 장애물(V63 실측 스트립) — 자동배치 회피·캔버스 표시. */
export interface HallObstacleDto {
  obstacleId: string;
  kind: 'stair' | 'elevator' | 'door' | 'wall';
  x0: number;
  y0: number;
  x1: number;
  y1: number;
}
/** 홀 마스터 정보(M2 LayoutDto.hall) — 캔버스 축척·경계 원천. dimsM=[폭, 깊이](m). */
export interface HallInfoDto {
  hallId: string;
  label?: string | null;
  dimsM?: number[] | null;
  ceilingM?: number | null;
  targetBoothCount?: number | null;
  pillars?: HallPillarDto[] | null;
  obstacles?: HallObstacleDto[] | null;
  trenches?: HallTrenchDto[] | null;
}
export interface LayoutDto {
  layoutId: string;
  eventId: string;
  hallId: string;
  version: number;
  name: string;
  status: string;
  booths: BoothDto[];
  hall?: HallInfoDto | null;
  summary: LayoutSummary;
  updatedAt: string;
}
export interface LayoutSaveRequest {
  name: string;
  version: number;
  booths: BoothDto[];
}

// 규정검증 리포트 (§3-validate / §4 precheck 공통 shape)
export type ViolationSeverity = 'block' | 'warn';
export interface Violation {
  pin: number;
  code: string;
  group: string;
  label: string;
  severity: ViolationSeverity;
  measured: string;
  requiresDocument?: string;
}
export interface ComplianceReport {
  rulesetVersion: string;
  disclaimer: string;
  blockCount: number;
  warnCount: number;
  passCount: number;
  submittable: boolean;
  violations: Violation[];
}

// AI 자동배치
export interface AutoLayoutRequest {
  targetBoothCount: number;
  premiumRatio: number;
  stageCount: number;
  loungeCount: number;
  mainEntranceCount: number;
  optionCount: number;
  /** 선택 구역(소분류, hall_zone.id 예 "H1-A"). null/미지정이면 홀 전체(2026-07-14 트리 선택). */
  zoneId?: string | null;
}
export interface AutoLayoutOption {
  optionId: string;
  label: string;
  summary: LayoutSummary;
  booths: BoothDto[]; // apply-option 저장 페이로드 — "이 안으로 편집 시작" 실배선
  s7RenderJobId: string | null; // 항상 null — 이미지는 버튼 클릭 시 previewS7로만 생성(2026-07-14)
  aiSummary?: string; // ai=true 시에만: 엔진 metrics 근거 AI 장단점 요약(WISE AI). 없으면 미표시.
}

// 자연어 조건 해석(POST /auto-generate/interpret)
export interface LayoutInterpretRequest {
  text: string;
}
export interface LayoutInterpretResult {
  request: AutoLayoutRequest; // 서버 클램핑된 조건(폼 초기값). 최종 권위는 사용자 폼.
  interpretedNote: string; // 사람이 읽는 해석 근거
  degraded: boolean; // true 면 AI 미가용/파싱 실패 → 수동 폼 폴백
  provider: string; // "claude" | "ollama" | "none"
}

// ── 4. M3 부스 설계 스튜디오 (SCR-06/09) ──
// S7 홀 전경 이미지 생성 발행 결과(버튼 클릭 전용, POST /auto-generate/preview)
export interface S7PreviewResult {
  jobId: string | null;
  degraded: boolean;
}

// 평면도 선택 트리 — 대분류(전시장) > 중분류(홀) > 소분류(구역) (GET /api/halls/tree, V60)
export interface HallTreeZone {
  id: string;
  label: string;
}
export interface HallTreeHall {
  id: string;
  label: string;
  dimsM: number[];
  zones: HallTreeZone[];
}
export interface HallTreeCenter {
  center: number;
  label: string;
  halls: HallTreeHall[];
}
export interface HallTreeDto {
  centers: HallTreeCenter[];
}

export interface DesignZone {
  type: string; // demo | consult | reception | storage …
  ratioPercent: number;
}
export interface DesignRigging {
  use: boolean;
  heightM: number;
}
export interface DesignMaterial {
  part: string; // wall | floor | ceiling …
  finish: string;
  fireRetardant: boolean;
}
export interface DesignSpec {
  boothType: string;
  industry: string;
  budget: number;
  zones: DesignZone[];
  wallHeightM: number;
  signageText: string;
  rigging: DesignRigging;
  mezzanineAreaRatio: number;
  materials: DesignMaterial[];
  lightingMode: string; // day | night
  usesDesignatedLightingOnly: boolean;
}
export interface DesignPlanDto {
  designId: string;
  boothId: string;
  version: number;
  status: string;
  spec: DesignSpec;
  updatedAt: string;
}
export interface DesignSaveRequest {
  version: number;
  spec: DesignSpec;
}

// ── 5. M4 유틸리티 (SCR-07/08) ──
export interface UtilityDevice {
  name: string;
  count: number;
  powerKw: number;
}
export interface UtilityQuoteRequest {
  devices: UtilityDevice[];
  networkWiredLines: number;
  plumbingOutlets: number;
  compressedAirOutlets: number;
}
export interface QuoteLine {
  label: string;
  qty: number;
  unitPrice: number;
  amount: number;
}
export interface UtilityQuote {
  totalPowerKw: number;
  requestedKw: number;
  distributionBox50A: number;
  lines: QuoteLine[];
  total: number;
  currency: string;
  rulesetVersion: string;
  disclaimer: string;
}
export type WiringKind = 'power' | 'network' | 'plumbing' | 'air';
export interface WiringTerminal {
  kind: WiringKind | string;
  position: [number, number]; // 홀 로컬 좌표(미터)
  kw?: number;
}
export interface WiringRequest {
  terminals: WiringTerminal[];
}
export interface WiringPath {
  kind: WiringKind | string;
  color: 'red' | 'blue' | 'green' | string;
  coords: number[][]; // [[x,y],…] 홀 로컬 좌표(미터)
  lengthM: number;
  label: string;
}
export interface WiringResult {
  assumedTrench: boolean;
  paths: WiringPath[];
}
export interface UtilityOrderDto {
  orderId: string;
  status: string;
  quote: UtilityQuote;
  locationDiagramUrl: string;
  supplyTiming: string;
  deadlineNotice: string;
  relayNotice: string;
}

// ── 6. M5 RenderJob (SCR-06/12) ──
export type RenderStatus = 'QUEUED' | 'RUNNING' | 'DONE' | 'FAILED';

// RenderJob 발행 요청 scene (계약 §6 — PLANNING §6-2 스키마)
export interface RenderScene {
  hall: { id: string; dims_m: [number, number]; ceiling_m: number };
  booth: { id: string; size_m: [number, number]; type: string };
  design?: { signage?: { text?: string } };
  lighting?: { mode: string };
}
export interface RenderJobRequest {
  shotPreset: string;
  scene: RenderScene;
  referenceImageUrl?: string;
}
export interface RenderJobDto {
  jobId: string;
  boothId: string;
  shotPreset: string;
  status: RenderStatus;
  imageUrl: string | null;
  schemaHash: string | null;
  modelVersion: string | null;
  watermarkRequired: boolean;
  watermarkText: string;
  notice: string;
  errorMessage: string | null;
  createdAt: string;
}

// ── 7. 로그인 슬라이드 (SCR-01 좌측 캐러셀 · 관리자 M18) ──
// 공개 GET /api/public/login-slides — 활성만, 최신 등록순(created_at DESC) 최대 5건.
export interface LoginSlideDto {
  id: number;
  imageUrl: string;
  title: string;
  caption: string | null;
  sortOrder: number | null;
  active: boolean;
  createdAt: string;
  updatedAt: string;
}
// 관리자 PUT /api/admin/login-slides/{id} — 문구·정렬·활성 여부 수정.
export interface LoginSlideUpdateRequest {
  title: string;
  caption?: string | null;
  sortOrder?: number | null;
  active?: boolean;
}

/* ─────────────────────────────────────────────────────────
 * §5B 공통 업무 기능 (SCR-39~48) — 백엔드 com.zioinfo.kintex.work.*
 * ★ 각 DTO는 대응 컨트롤러/레코드 시그니처와 정확히 일치(추측 금지).
 * ───────────────────────────────────────────────────────── */

// ── SCR-39 업무일지 (work/worklog) ──
export interface WorklogDto {
  id: string;
  eventId: string | null;
  writerId: string;
  writerName: string;
  workDate: string; // YYYY-MM-DD
  title: string;
  workType: string | null;
  progress: string | null;
  status: string | null;
  content: string | null;
  hours: number;
  createdAt: string;
  updatedAt: string;
}
export interface WorklogSaveRequest {
  eventId?: string | null;
  workDate: string;
  title: string;
  workType?: string | null;
  progress?: string | null;
  status?: string | null;
  content?: string | null;
  hours?: number | null;
}

// ── SCR-40 통합 일정 (work/schedule) ──
export interface ScheduleDto {
  id: string;
  eventId: string | null;
  ownerId: string;
  ownerName: string;
  title: string;
  scheduleType: string | null;
  importance: string | null;
  startAt: string; // ISO-8601
  endAt: string | null;
  allDay: boolean;
  location: string | null;
  content: string | null;
  color: string | null;
  createdAt: string;
}
export interface ScheduleSaveRequest {
  eventId?: string | null;
  title: string;
  scheduleType?: string | null;
  importance?: string | null;
  startAt: string;
  endAt?: string | null;
  allDay?: boolean | null;
  location?: string | null;
  content?: string | null;
  color?: string | null;
}

// ── SCR-41 쪽지 (work/message) ──
export interface MessageDto {
  id: string;
  senderId: string;
  senderName: string;
  title: string;
  content: string;
  recvType: string | null; // RECV | REF (수신함 뷰)
  readAt: string | null;
  createdAt: string;
}
export interface MessageSendRequest {
  title?: string | null;
  content?: string | null;
  recipientIds: string[]; // RECV
  refIds?: string[]; // REF
}

// ── SCR-42 공지 (work/notice) ──
export interface NoticeDto {
  id: string;
  eventId: string | null;
  category: string | null;
  title: string;
  content: string | null;
  pinned: boolean;
  authorId: string;
  authorName: string;
  viewCount: number;
  publishedAt: string | null;
  createdAt: string;
}
export interface NoticeSaveRequest {
  eventId?: string | null;
  category?: string | null;
  title: string;
  content?: string | null;
  pinned?: boolean | null;
}

// ── SCR-43 의견접수 (work/opinion) ──
export interface OpinionDto {
  id: string;
  eventId: string | null;
  category: string | null;
  title: string;
  content: string | null;
  status: string; // 접수 RECEIVED | 검토중 REVIEWING | 답변완료 ANSWERED (백엔드 권위)
  authorId: string;
  authorName: string;
  secretYn: string | null;
  createdAt: string;
}
export interface OpinionCommentDto {
  id: string;
  opinionId: string;
  authorId: string;
  authorName: string;
  content: string;
  createdAt: string;
}
export interface OpinionDetail {
  opinion: OpinionDto;
  comments: OpinionCommentDto[];
}
export interface OpinionSaveRequest {
  eventId?: string | null;
  category?: string | null;
  title: string;
  content?: string | null;
  secretYn?: string | null;
}

// ── SCR-44 통합검색 (work/search) ──
// type: WORKLOG | NOTICE | MEETING | REPORT | OPINION
export type SearchResultType = 'WORKLOG' | 'NOTICE' | 'MEETING' | 'REPORT' | 'OPINION' | string;
export interface SearchResultItem {
  type: SearchResultType;
  id: string;
  title: string;
  createdAt: string;
}

// ── SCR-45 회의록 (work/meeting) ──
export interface MeetingDto {
  id: string;
  eventId: string | null;
  title: string;
  location: string | null;
  meetingAt: string; // ISO-8601
  organizerId: string;
  organizerName: string;
  content: string | null;
  minutes: string | null;
  createdAt: string;
}
export interface MeetingActionDto {
  id: string;
  meetingId: string;
  seq: number;
  actionItem: string;
  assigneeId: string | null;
  dueDate: string | null;
  status: string; // OPEN | DONE …
}
export interface MeetingDetail {
  meeting: MeetingDto;
  actions: MeetingActionDto[];
}
export interface MeetingSaveRequest {
  eventId?: string | null;
  title: string;
  location?: string | null;
  meetingAt: string;
  content?: string | null;
}
export interface MeetingActionRequest {
  actionItem: string;
  assigneeId?: string | null;
  dueDate?: string | null;
}

// ── SCR-46 업무보고 (work/report) + 통계 (work/stats) ──
export type ReportType = 'DAILY' | 'WEEKLY' | 'MONTHLY' | string;
export interface ReportDto {
  id: string;
  eventId: string | null;
  reportType: string;
  periodFrom: string | null;
  periodTo: string | null;
  title: string;
  content: string | null;
  authorId: string;
  authorName: string;
  createdAt: string;
}
export interface ReportSaveRequest {
  eventId?: string | null;
  reportType?: string | null;
  periodFrom?: string | null;
  periodTo?: string | null;
  title: string;
  content?: string | null;
}
export interface StatItem {
  name: string;
  count: number;
  hours: number;
}
export interface WorkStatsDto {
  totalCount: number;
  totalHours: number;
  byStatus: StatItem[];
  byType: StatItem[];
  byWriter: StatItem[];
}

// ── SCR-47 알림센터 (work/notification) ──
export interface NotificationDto {
  id: string;
  recipientId: string;
  eventId: string | null;
  notiType: string;
  title: string;
  message: string | null;
  link: string | null;
  readAt: string | null;
  createdAt: string;
}

/* ─────────────────────────────────────────────────────────
 * 대시보드 계열 집계 (SCR-02·13·14·16)
 * 정본: 백엔드 record — com.zioinfo.kintex.{dashboard,analytics,admin,ops}.dto.*
 * 봉투 내부 data. 원천 부재 지표는 백엔드가 0/null/빈배열로 정직 반환한다.
 * ───────────────────────────────────────────────────────── */

// ── SCR-02 주최자 대시보드 (dashboard.DashboardDto) ──
export interface DashboardMilestone {
  label: string;
  date: string; // "YYYY-MM-DD"
  state: 'done' | 'current' | 'upcoming' | string;
}
export interface DashboardKpi {
  label: string;
  value: string;
  delta: string | null;
  trend: 'up' | 'down' | 'flat' | string;
}
export interface DashboardExhibitor {
  companyName: string;
  boothNo: string;
  status: string | null;
}
export interface DashboardFeed {
  id: string;
  kind: string; // "render" 등
  message: string;
  at: string; // ISO
}
export interface DashboardData {
  milestones: DashboardMilestone[];
  kpis: DashboardKpi[];
  exhibitors: DashboardExhibitor[];
  feed: DashboardFeed[];
}

// ── SCR-13 경영분석 BI (analytics.AnalyticsDto) ──
export type AnalyticsPerspective = 'operator' | 'exhibitor';
export type AnalyticsPeriod = 'event' | 'd90' | 'annual';
export interface AnalyticsKpi {
  label: string;
  value: string;
  delta: string | null;
  trend: 'up' | 'down' | 'flat' | string;
}
export interface AnalyticsTrendPoint {
  period: string; // "YYYY-MM"
  revenue: number;
  booths: number;
}
export interface AnalyticsSector {
  name: string;
  sharePercent: number;
  revenue: number;
}
export interface AnalyticsPnlRow {
  item: string;
  amount: number;
  grade: 'A' | 'B' | 'C' | string;
}
export interface AnalyticsExhibitorPerf {
  companyName: string;
  leads: number;
  conversionPercent: number;
}
export interface AnalyticsData {
  kpis: AnalyticsKpi[];
  trend: AnalyticsTrendPoint[];
  sectors: AnalyticsSector[];
  pnl: AnalyticsPnlRow[];
  exhibitorPerf: AnalyticsExhibitorPerf[];
}

// ── SCR-14 관리자 백오피스 (admin.AdminDashboardDto) ──
export interface AdminKpiDto {
  label: string;
  value: string;
  sub: string | null;
}
export interface AdminVisitorPoint {
  hour: string;
  count: number;
}
export interface AdminLiveEvent {
  eventId: string;
  name: string;
  hall: string | null;
  occupancy: number;
  status: string;
}
export interface AdminDashboardData {
  kpis: AdminKpiDto[];
  visitorTrend: AdminVisitorPoint[];
  liveEvents: AdminLiveEvent[];
  tenants: string[];
}

// ── SCR-16 관리자 집계 지표 (admin.AdminStatsDto) — 실 테이블 존재분만 ──
export interface AdminStats {
  totalUsers: number;
  activeUsers: number;
  totalEvents: number;
  ongoingEvents: number;
  upcomingEvents: number;
  tenantCount: number;
  companyCount: number;
  todayCheckins: number;
}

// ── SCR-16 홀 현장 운영 (ops.OpsDto) ──
export interface OpsHall {
  hallId: string;
  label: string;
  estPeople: number;
  heat: 'smooth' | 'moderate' | 'busy' | string;
}
export interface OpsHvac {
  tempC: number | null;
  humidityPercent: number | null;
  co2Ppm: number | null;
}
export interface OpsLightingZone {
  zone: string;
  onPercent: number;
}
export interface OpsParkingZone {
  zone: string;
  occupancy: number;
  capacity: number;
}
export interface OpsData {
  halls: OpsHall[];
  hvac: OpsHvac;
  lighting: OpsLightingZone[];
  parking: OpsParkingZone[];
}
