// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

/*
generator erd {
  provider = "prisma-erd-generator"
  output   = "../public/erd.svg"
}
*/

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

// ==== Enums ====
enum BoardStatus {
  active
  hidden
  archived
}


enum AccessLevel {
  public // 비회원도 접근
  member // 로그인 필요
  moderator // 운영진 이상
  admin // 관리자만
}

enum PostStatus {
  published
  hidden
  deleted
}

enum AttachmentType {
  image
  file
}

enum BoardModRole {
  MODERATOR
  MANAGER
}

enum UserStatus {
  active
  suspended
  withdrawn
}

enum AuthLevel {
  USER
  MOD
  ADMIN
}

enum ReactionType {
  RECOMMEND
  REPORT
}

enum SanctionType {
  SUSPEND
  BAN
  DOWNGRADE
}

enum TargetType {
  POST
  COMMENT
  USER
  BOARD
  SYSTEM
}

// RBAC 리소스/액션 정의
enum Resource {
  BOARD
  POST
  COMMENT
  USER
  ADMIN
}

enum Action {
  READ
  CREATE
  UPDATE
  DELETE
  MODERATE
  ADMINISTER
}

// ==== Models ====

// 게시판 대분류
model BoardCategory {
  id        String   @id @default(cuid())
  name      String   @unique
  slug      String   @unique
  sortOrder Int      @default(0)
  status    String   @default("active")

  boards    Board[]

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([status, sortOrder])
  @@map("board_categories")
}

// 게시판 정의(관리자 생성/정렬)
model Board {
  id                 String      @id @default(cuid())
  name               String
  slug               String      @unique // URL용
  description        String?
  sortOrder          Int         @default(0)
  status             BoardStatus @default(active)
  allowAnonymousPost Boolean     @default(false) // 익명 글 허용
  allowSecretComment Boolean     @default(false) // 비밀댓글 허용
  isAdultOnly        Boolean     @default(false) // 성인 인증 필요 여부
  requiredTags       Json?
  requiredFields     Json?

  readLevel  AccessLevel @default(public) // 읽기 권한
  writeLevel AccessLevel @default(member) // 글쓰기 권한

  // 대분류
  categoryId String?
  category   BoardCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull)

  // 뷰 타입 설정 (동적 테이블 참조)
  mainPageViewTypeId String?
  mainPageViewType   BoardViewType? @relation("MainPageViewType", fields: [mainPageViewTypeId], references: [id], onDelete: SetNull)
  listViewTypeId     String?
  listViewType       BoardViewType? @relation("ListViewType", fields: [listViewTypeId], references: [id], onDelete: SetNull)

  posts      Post[]
  moderators BoardModerator[]

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([status, sortOrder])
  @@index([categoryId])
  @@index([mainPageViewTypeId])
  @@index([listViewTypeId])
  @@map("boards")
}

// 게시판 뷰 타입 정의 (enum 대신 데이터 테이블)
model BoardViewType {
  id    String @id @default(cuid())
  key   String @unique          // 예: preview, text, special_rank
  name  String                  // 표시용 이름
  scope String                  // 용도 구분: 'main' | 'list' 등 자유 텍스트

  // 역참조
  mainBoards Board[] @relation("MainPageViewType")
  listBoards Board[] @relation("ListViewType")

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([scope])
  @@map("board_view_types")
}

// 게시판 운영진 매핑
model BoardModerator {
  id      String       @id @default(cuid())
  boardId String
  userId  String
  role    BoardModRole @default(MODERATOR)

  board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
  user  User  @relation(fields: [userId], references: [userId], onDelete: Cascade)

  createdAt DateTime @default(now())

  @@unique([boardId, userId]) // 한 게시판당 1회 매핑
  @@index([userId])
  @@map("board_moderators")
}

// 게시글(게시판 내 컨텐츠)
model Post {
  id          String     @id @default(cuid())
  boardId     String
  authorId    String?
  title       String
  content     String
  status      PostStatus @default(published)
  isAnonymous Boolean    @default(false)

  isPinned    Boolean @default(false)
  pinnedOrder Int?

  createdAt      DateTime @default(now())
  updatedAt      DateTime @updatedAt
  lastActivityAt DateTime @default(now())

  board       Board         @relation(fields: [boardId], references: [id], onDelete: Cascade)
  author      User?         @relation(fields: [authorId], references: [userId], onDelete: SetNull)
  comments    Comment[]
  attachments Attachment[]
  postTags    PostTag[]
  reactions   Reaction[]
  reports     Report[]
  stat        PostStat?
  viewLogs    PostViewLog[]

  @@index([boardId, status, createdAt])
  @@index([boardId, isPinned, pinnedOrder])
  @@map("posts")
}

// 사용자
model User {
  userId       String   @id
  nickname     String   @unique
  passwordHash String?
  name         String
  birth        DateTime?
  phone        String?  @unique
  rank         Int      @default(0)
  // 누적 포인트, 레벨, 등급(0~10)
  points       Int      @default(0)
  level        Int      @default(1)
  grade        Int      @default(0)

  status           UserStatus @default(active)
  authLevel        AuthLevel  @default(USER)
  profileImage     String?
  lastLoginAt      DateTime?
  isAdultVerified  Boolean    @default(false)
  loginFailCount   Int        @default(0)
  agreementTermsAt DateTime

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  posts               Post[]
  boardModerations    BoardModerator[]
  comments            Comment[]
  userRoles           UserRole[]
  reportsFiled        Report[]             @relation("ReportReporter")
  reactions           Reaction[]           @relation("UserReactions")
  messagesSent        Message[]            @relation("MessageSender")
  messagesReceived    Message[]            @relation("MessageReceiver")
  blocksInitiated     Block[]              @relation("Blocker")
  blocksReceived      Block[]              @relation("Blocked")
  pointTxns           PointTransaction[]
  attendances         Attendance[]
  sanctions           Sanction[]
  nicknameChanges     NicknameChange[]
  passwordResetTokens PasswordResetToken[] @relation("PasswordResetUser")
  auditLogs           AuditLog[]           @relation("AuditActor")
  loginSessions       LoginSession[]
  adminNotifications  AdminNotification[]  @relation("AdminNotificationCreator")
  ipBlocksCreated     IPBlock[]            @relation("IPBlockCreator")
  postViewLogs        PostViewLog[]        @relation("PostViewLogUser")
  couponRedemptions   CouponRedemption[]

  @@index([status])
  @@index([createdAt])
  @@map("users")
}

// 역할 정의 (RBAC)
model Role {
  roleId      String  @id @default(cuid())
  name        String  @unique
  description String?

  userRoles    UserRole[]
  permissions  RolePermission[]

  createdAt DateTime @default(now())

  @@map("roles")
}

// 역할별 권한 매핑 (리소스×액션)
model RolePermission {
  id        String   @id @default(cuid())
  roleId    String
  resource  Resource
  action    Action
  allowed   Boolean  @default(true)
  createdAt DateTime @default(now())

  role      Role     @relation(fields: [roleId], references: [roleId], onDelete: Cascade)

  @@unique([roleId, resource, action])
  @@index([resource, action])
  @@map("role_permissions")
}

// 사용자-역할 매핑 (다대다)
model UserRole {
  id     String @id @default(cuid())
  userId String
  roleId String

  user User @relation(fields: [userId], references: [userId], onDelete: Cascade)
  role Role @relation(fields: [roleId], references: [roleId], onDelete: Cascade)

  createdAt DateTime @default(now())

  @@unique([userId, roleId])
  @@index([roleId])
  @@map("user_roles")
}

// 댓글
model Comment {
  id                 String   @id @default(cuid())
  postId             String
  parentId            String?  // 부모 댓글 ID (null이면 최상위 댓글)
  depth               Int      @default(0) // 댓글 깊이 (0=최상위, 1=1단계 대댓글, 2=2단계 대댓글)
  authorId           String?
  content            String
  isAnonymous        Boolean  @default(false)
  isSecret           Boolean  @default(false)
  secretPasswordHash String?
  createdAt          DateTime @default(now())
  updatedAt          DateTime @updatedAt

  post       Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
  parent     Comment? @relation("CommentReplies", fields: [parentId], references: [id], onDelete: Cascade)
  replies    Comment[] @relation("CommentReplies")
  author     User?    @relation(fields: [authorId], references: [userId], onDelete: SetNull)
  reports     Report[]

  @@index([postId, createdAt])
  @@index([parentId])
  @@map("comments")
}

// 태그 및 매핑
model Tag {
  tagId     String    @id @default(cuid())
  name      String    @unique
  slug      String    @unique
  postTags  PostTag[]
  createdAt DateTime  @default(now())

  @@map("tags")
}

model PostTag {
  id     String @id @default(cuid())
  postId String
  tagId  String

  post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
  tag  Tag  @relation(fields: [tagId], references: [tagId], onDelete: Cascade)

  @@unique([postId, tagId])
  @@index([tagId])
  @@map("post_tags")
}

// 첨부파일(이미지/파일)
model Attachment {
  id        String         @id @default(cuid())
  postId    String
  url       String
  type      AttachmentType @default(image)
  size      Int?
  width     Int?
  height    Int?
  sortOrder Int?

  post Post @relation(fields: [postId], references: [id], onDelete: Cascade)

  createdAt DateTime @default(now())

  @@index([postId])
  @@map("attachments")
}

// 반응(추천/신고)
model Reaction {
  id         String       @id @default(cuid())
  postId     String
  userId     String?
  clientHash String?
  type       ReactionType @default(RECOMMEND)
  createdAt  DateTime     @default(now())

  post Post  @relation(fields: [postId], references: [id], onDelete: Cascade)
  user User? @relation("UserReactions", fields: [userId], references: [userId], onDelete: SetNull)

  @@unique([postId, type, userId, clientHash])
  @@index([postId, type])
  @@index([userId])
  @@index([clientHash])
  @@map("reactions")
}

// 게시글 열람 로그(게시판별 열람 로그 요구)
model PostViewLog {
  id        String   @id @default(cuid())
  postId    String
  userId    String?
  ip        String?
  userAgent String?
  createdAt DateTime @default(now())

  post Post  @relation(fields: [postId], references: [id], onDelete: Cascade)
  user User? @relation("PostViewLogUser", fields: [userId], references: [userId], onDelete: SetNull)

  @@index([postId, createdAt])
  @@index([userId])
  @@map("post_view_logs")
}

// 게시글 통계 카운터
model PostStat {
  postId         String @id
  views          Int    @default(0)
  recommendCount Int    @default(0)
  reportCount    Int    @default(0)
  commentsCount  Int    @default(0)

  post Post @relation(fields: [postId], references: [id], onDelete: Cascade)

  @@map("post_stats")
}

// 신고(게시글/댓글 대상)
model Report {
  id         String     @id @default(cuid())
  reporterId String?
  targetType TargetType
  postId     String?
  commentId  String?
  reason     String
  status     String     @default("open")
  createdAt  DateTime   @default(now())

  reporter User?    @relation("ReportReporter", fields: [reporterId], references: [userId], onDelete: SetNull)
  post     Post?    @relation(fields: [postId], references: [id], onDelete: Cascade)
  comment  Comment? @relation(fields: [commentId], references: [id], onDelete: Cascade)

  @@index([targetType])
  @@index([postId])
  @@index([commentId])
  @@map("reports")
}

// 쪽지(DM)
model Message {
  id         String    @id @default(cuid())
  senderId   String
  receiverId String
  body       String
  readAt     DateTime?
  createdAt  DateTime  @default(now())

  sender   User @relation("MessageSender", fields: [senderId], references: [userId], onDelete: Cascade)
  receiver User @relation("MessageReceiver", fields: [receiverId], references: [userId], onDelete: Cascade)

  @@index([senderId, createdAt])
  @@index([receiverId, createdAt])
  @@map("messages")
}

// 차단
model Block {
  id        String   @id @default(cuid())
  blockerId String
  blockedId String
  createdAt DateTime @default(now())

  blocker User @relation("Blocker", fields: [blockerId], references: [userId], onDelete: Cascade)
  blocked User @relation("Blocked", fields: [blockedId], references: [userId], onDelete: Cascade)

  @@unique([blockerId, blockedId])
  @@index([blockedId])
  @@map("blocks")
}

// 포인트 트랜잭션
model PointTransaction {
  id            String      @id @default(cuid())
  userId        String
  amount        Int // + 적립, - 차감
  reason        String
  referenceType TargetType?
  referenceId   String?
  createdAt     DateTime    @default(now())

  user User @relation(fields: [userId], references: [userId], onDelete: Cascade)

  @@index([userId, createdAt])
  @@map("point_transactions")
}

// 출석부 기록 (사용자별 일자 단위 출석)
model Attendance {
  id        String   @id @default(cuid())
  userId    String
  date      DateTime // 자정 기준 날짜만 사용
  createdAt DateTime @default(now())

  user User @relation(fields: [userId], references: [userId], onDelete: Cascade)

  @@unique([userId, date])
  @@index([userId, date])
  @@map("attendance")
}

// 레벨 임계치(선택)
model LevelThreshold {
  id        String @id @default(cuid())
  level     Int    @unique
  minPoints Int

  @@map("level_thresholds")
}

// 금칙어 정책
model BannedKeyword {
  id        String     @id @default(cuid())
  pattern   String     @unique
  appliesTo TargetType // POST/COMMENT/MESSAGE 등
  severity  Int        @default(1)
  active    Boolean    @default(true)
  createdAt DateTime   @default(now())

  @@index([appliesTo])
  @@map("banned_keywords")
}

// 제재 이력
model Sanction {
  id        String       @id @default(cuid())
  userId    String
  type      SanctionType
  reason    String
  startAt   DateTime     @default(now())
  endAt     DateTime?
  createdBy String?

  user User @relation(fields: [userId], references: [userId], onDelete: Cascade)

  @@index([userId, type])
  @@map("sanctions")
}

// 감사 로그
model AuditLog {
  id         String      @id @default(cuid())
  actorId    String?
  action     String
  targetType TargetType?
  targetId   String?
  meta       Json?
  createdAt  DateTime    @default(now())

  actor User? @relation("AuditActor", fields: [actorId], references: [userId], onDelete: SetNull)

  @@index([action, createdAt])
  @@map("audit_logs")
}

// 관리자 알림함(신고/포인트 이상/후기 등 이벤트)
model AdminNotification {
  id         String      @id @default(cuid())
  type       String
  message    String
  targetType TargetType?
  targetId   String?
  createdBy  String?
  createdAt  DateTime    @default(now())
  readAt     DateTime?

  createdByUser User? @relation("AdminNotificationCreator", fields: [createdBy], references: [userId], onDelete: SetNull)

  @@index([type, createdAt])
  @@map("admin_notifications")
}

// 로그인 기기/최근 접속 IP 기록
model LoginSession {
  id         String   @id @default(cuid())
  userId     String
  device     String?
  ip         String?
  userAgent  String?
  createdAt  DateTime @default(now())
  lastSeenAt DateTime @default(now())

  user User @relation(fields: [userId], references: [userId], onDelete: Cascade)

  @@index([userId, lastSeenAt])
  @@map("login_sessions")
}

// IP 차단 목록
model IPBlock {
  id        String   @id @default(cuid())
  ip        String
  reason    String?
  active    Boolean  @default(true)
  createdBy String?
  createdAt DateTime @default(now())

  createdByUser User? @relation("IPBlockCreator", fields: [createdBy], references: [userId], onDelete: SetNull)

  @@unique([ip, active])
  @@map("ip_blocks")
}

// 닉네임 변경 이력
model NicknameChange {
  id          String   @id @default(cuid())
  userId      String
  oldNickname String
  newNickname String
  changedAt   DateTime @default(now())

  user User @relation(fields: [userId], references: [userId], onDelete: Cascade)

  @@index([userId, changedAt])
  @@map("nickname_changes")
}

// 인증 보조 - 비밀번호 재설정 토큰
model PasswordResetToken {
  id        String    @id @default(cuid())
  userId    String
  token     String    @unique
  expiresAt DateTime
  usedAt    DateTime?
  createdAt DateTime  @default(now())

  user User @relation("PasswordResetUser", fields: [userId], references: [userId], onDelete: Cascade)

  @@index([userId, expiresAt])
  @@map("password_reset_tokens")
}

// 쿠폰
model Coupon {
  id          String   @id @default(cuid())
  code        String   @unique
  title       String
  description String?
  stock       Int      @default(0) // 총 재고
  maxPerUser  Int      @default(1) // 1인당 제한
  expiresAt   DateTime?
  active      Boolean  @default(true)
  createdAt   DateTime @default(now())

  redemptions CouponRedemption[]

  @@index([active, expiresAt])
  @@map("coupons")
}

model CouponRedemption {
  id        String   @id @default(cuid())
  couponId  String
  userId    String
  createdAt DateTime @default(now())

  coupon Coupon @relation(fields: [couponId], references: [id], onDelete: Cascade)
  user   User   @relation(fields: [userId], references: [userId], onDelete: Cascade)

  @@unique([couponId, userId])
  @@index([userId, createdAt])
  @@map("coupon_redemptions")
}

// 제휴업체(위치 기반)
model Partner {
  id        String   @id @default(cuid())
  name      String   @unique
  category  String
  categoryId String?
  latitude  Float
  longitude Float
  address   String?
  imageUrl  String?
  sortOrder Int      @default(0)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  categoryRef PartnerCategory? @relation(fields: [categoryId], references: [id])

  @@index([category])
  @@index([categoryId])
  @@index([sortOrder])
  @@map("partners")
}

// 제휴업체 카테고리(관리자 생성/삭제)
model PartnerCategory {
  id        String   @id @default(cuid())
  name      String   @unique
  sortOrder Int      @default(0)
  createdAt DateTime @default(now())

  partners Partner[]

  @@index([sortOrder])
  @@map("partner_categories")
}

// 배너/공지 노출용
model Banner {
  id        String   @id @default(cuid())
  title     String
  imageUrl  String
  linkUrl   String?
  active    Boolean  @default(true)
  sortOrder Int      @default(0)
  startAt   DateTime?
  endAt     DateTime?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([active, sortOrder])
  @@index([startAt, endAt])
  @@map("banners")
}

// 제휴 문의
model PartnerInquiry {
  id         String   @id @default(cuid())
  name       String
  contact    String
  category   String?
  message    String
  status     String   @default("pending") // pending/approved/rejected
  createdAt  DateTime @default(now())
  approvedAt DateTime?

  @@index([status, createdAt])
  @@map("partner_inquiries")
}

// 제휴업소 등록 요청
model PartnerRequest {
  id         String   @id @default(cuid())
  name       String
  category   String
  latitude   Float
  longitude  Float
  address    String?
  contact    String?
  region     String?
  imageUrl   String?
  sortOrder  Int      @default(0)
  active     Boolean  @default(true)
  status     String   @default("pending") // pending/approved/rejected
  createdAt  DateTime @default(now())
  approvedAt DateTime?

  @@index([status, createdAt])
  @@map("partner_requests")
}

// 시스템 설정 (key-value 형태)
model Setting {
  id        String   @id @default(cuid())
  key       String   @unique
  value     String   // JSON 문자열로 저장
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@map("settings")
}

// 메인 노출용 제휴 샵 가로 스크롤 데이터
// PartnerShop 모델 제거됨 (PartnerRequest로 통합)
