const { PrismaClient } = require("@prisma/client");

const prisma = new PrismaClient();

function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function randomDate(startYear, endYear) {
  const start = new Date(`${startYear}-01-01`).getTime();
  const end = new Date(`${endYear}-12-31`).getTime();
  return new Date(randomInt(start, end));
}

function generateRandomKoreanName() {
  const lastNames = ["김", "이", "박", "최", "정", "강", "조", "윤", "장", "임"];
  const firstParts = ["민", "서", "도", "현", "지", "아", "윤", "준", "하", "유", "채", "은", "수", "태", "나"]; 
  const secondParts = ["우", "영", "민", "서", "진", "현", "빈", "율", "솔", "연", "환", "호", "린", "훈", "경"]; 
  const last = lastNames[randomInt(0, lastNames.length - 1)];
  const first = firstParts[randomInt(0, firstParts.length - 1)] + secondParts[randomInt(0, secondParts.length - 1)];
  return last + first;
}

function generateUniquePhone(i) {
  const mid = String(2000 + (i % 8000)).padStart(4, "0");
  const last = String(3000 + i).padStart(4, "0");
  return `010-${mid}-${last}`;
}

function generateNickname(i) {
  const suffix = Math.random().toString(36).slice(2, 4);
  return `user${String(i + 1).padStart(3, "0")}${suffix}`;
}

async function createRandomUsers(count = 100) {
  const roleUser = await prisma.role.findUnique({ where: { name: "user" } });
  // 사용되지 않은 전화번호를 찾는 보조 함수
  async function findAvailablePhone(startIndex) {
    let offset = 0;
    while (true) {
      const candidate = generateUniquePhone(startIndex + offset);
      const exists = await prisma.user.findUnique({ where: { phone: candidate } });
      if (!exists) return candidate;
      offset += 1;
    }
  }

  for (let i = 0; i < count; i++) {
    // 닉네임을 결정론적으로 생성해 재실행 시 중복 생성 방지
    const nickname = `user${String(i + 1).padStart(3, "0")}`;
    const existing = await prisma.user.findUnique({ where: { nickname } });
    let user = existing;
    if (!existing) {
      const name = generateRandomKoreanName();
      const birth = randomDate(1975, 2005);
      const phone = await findAvailablePhone(i + 2); // admin이 0001 사용하므로 겹치지 않도록 오프셋
      user = await prisma.user.create({
        data: {
          nickname,
          name,
          birth,
          phone,
          agreementTermsAt: new Date(),
          authLevel: "USER",
          isAdultVerified: Math.random() < 0.6,
          lastLoginAt: Math.random() < 0.8 ? new Date() : null,
        },
      });
    }
    if (roleUser && user) {
      await prisma.userRole.upsert({
        where: { userId_roleId: { userId: user.userId, roleId: roleUser.roleId } },
        update: {},
        create: { userId: user.userId, roleId: roleUser.roleId },
      });
    }
  }
}

async function upsertCategories() {
  // 카테고리 트리 (projectmemo 기준 상위 그룹)
  const categories = [
    { name: "메인", slug: "main", sortOrder: 1, status: "active" },
    { name: "명예의 전당", slug: "hall-of-fame", sortOrder: 2, status: "active" },
    { name: "소통방", slug: "community", sortOrder: 3, status: "active" },
  ];
  const map = {};
  for (const c of categories) {
    const created = await prisma.boardCategory.upsert({
      where: { slug: c.slug },
      update: { name: c.name, sortOrder: c.sortOrder, status: c.status },
      create: c,
    });
    map[c.slug] = created;
  }
  return map; // { slug: category }
}

async function upsertRoles() {
  const roles = [
    { name: "admin", description: "관리자" },
    { name: "editor", description: "운영진" },
    { name: "user", description: "일반 사용자" }
  ];
  for (const r of roles) {
    await prisma.role.upsert({
      where: { name: r.name },
      update: { description: r.description },
      create: r,
    });
  }
  // 기본 권한 매핑
  const roleMap = {
    admin: [
      ["ADMIN", "ADMINISTER"],
      ["BOARD", "MODERATE"],
      ["POST", "CREATE"],
      ["POST", "UPDATE"],
      ["POST", "DELETE"],
      ["COMMENT", "DELETE"],
      ["USER", "UPDATE"],
    ],
    editor: [
      ["BOARD", "MODERATE"],
      ["POST", "UPDATE"],
      ["POST", "DELETE"],
      ["COMMENT", "DELETE"],
    ],
    user: [
      ["POST", "CREATE"],
      ["COMMENT", "CREATE"],
      ["POST", "READ"],
      ["COMMENT", "READ"],
    ],
  };
  for (const [roleName, perms] of Object.entries(roleMap)) {
    const role = await prisma.role.findUnique({ where: { name: roleName } });
    if (!role) continue;
    for (const [resource, action] of perms) {
      await prisma.rolePermission.upsert({
        where: {
          roleId_resource_action: {
            roleId: role.roleId,
            resource,
            action,
          },
        },
        update: { allowed: true },
        create: { roleId: role.roleId, resource, action, allowed: true },
      });
    }
  }
}

async function upsertAdmin() {
  const admin = await prisma.user.upsert({
    where: { nickname: "admin" },
    update: {},
    create: {
      nickname: "admin",
      name: "Administrator",
      birth: new Date("1990-01-01"),
      phone: "010-0000-0001",
      agreementTermsAt: new Date(),
      authLevel: "ADMIN",
    },
  });

  const adminRole = await prisma.role.findUnique({ where: { name: "admin" } });
  if (adminRole) {
    await prisma.userRole.upsert({
      where: {
        userId_roleId: { userId: admin.userId, roleId: adminRole.roleId },
      },
      update: {},
      create: { userId: admin.userId, roleId: adminRole.roleId },
    });
  }
  return admin;
}

async function upsertBoards(admin, categoryMap) {
  const boards = [
    // 일반
    { name: "공지사항", slug: "notice", description: "공지", sortOrder: 1, writeLevel: "moderator" },
    { name: "가입인사", slug: "greetings", description: "가입인사", sortOrder: 2 },
    { name: "버그건의", slug: "bug-report", description: "버그/건의", sortOrder: 3 },
    { name: "이벤트", slug: "event", description: "이벤트", sortOrder: 4, requiredTags: { required: ["이벤트"] } },
    { name: "자유게시판", slug: "free", description: "자유", sortOrder: 5 },
    { name: "무엇이든", slug: "qna", description: "무엇이든 물어보세요", sortOrder: 6 },
    { name: "익명게시판", slug: "anonymous", description: "익명", sortOrder: 8, allowAnonymousPost: true, allowSecretComment: true },
    { name: "청와대", slug: "blue-house", description: "레벨 제한", sortOrder: 10, readLevel: "member" },
    // 특수
    { name: "출석부", slug: "attendance", description: "데일리 체크인", sortOrder: 12 },
    { name: "회원랭킹", slug: "ranking", description: "랭킹", sortOrder: 14 },
    { name: "무료쿠폰", slug: "free-coupons", description: "쿠폰", sortOrder: 15 },
    { name: "월간집계", slug: "monthly-stats", description: "월간 통계", sortOrder: 16 },
    // 광고/제휴 계열 게시판은 제거(메인/명예의전당/소통방 외)
  ];

  const created = [];
  // 특수 랭킹/텍스트 뷰 타입 ID 조회 (사전에 upsertViewTypes로 생성됨)
  const mainSpecial = await prisma.boardViewType.findUnique({ where: { key: "main_special_rank" } });
  const listSpecial = await prisma.boardViewType.findUnique({ where: { key: "list_special_rank" } });
  const mainText = await prisma.boardViewType.findUnique({ where: { key: "main_text" } });

  for (const b of boards) {
    // 카테고리 매핑 규칙 (트리 기준 상위 카테고리)
    const mapBySlug = {
      // 암실소문 (메인)
      notice: "main",
      greetings: "main",
      "bug-report": "main",
      event: "main",
      attendance: "main",
      // 명예의 전당
      ranking: "hall-of-fame",
      "free-coupons": "hall-of-fame",
      "monthly-stats": "hall-of-fame",
      // 소통방(기본값 community로 처리)
      free: "community",
      qna: "community",
      tips: "community",
      anonymous: "community",
      // 광고/제휴
    };
    const categorySlug = mapBySlug[b.slug] || "community";
    const category = categoryMap[categorySlug];
    const board = await prisma.board.upsert({
      where: { slug: b.slug },
      update: {
        description: b.description,
        sortOrder: b.sortOrder,
        allowAnonymousPost: !!b.allowAnonymousPost,
        readLevel: b.readLevel || undefined,
        categoryId: category ? category.id : undefined,
        // 메인뷰: 기본이 아니라 텍스트(main_text)로 설정, 랭킹 보드는 특수 랭킹 유지
        ...(b.slug === "ranking"
          ? (mainSpecial ? { mainPageViewTypeId: mainSpecial.id } : {})
          : (mainText ? { mainPageViewTypeId: mainText.id } : {})),
        ...(b.slug === "ranking" && listSpecial ? { listViewTypeId: listSpecial.id } : {}),
      },
      create: {
        name: b.name,
        slug: b.slug,
        description: b.description,
        sortOrder: b.sortOrder,
        allowAnonymousPost: !!b.allowAnonymousPost,
        readLevel: b.readLevel || undefined,
        categoryId: category ? category.id : undefined,
        // 메인뷰: 기본이 아니라 텍스트(main_text)로 설정, 랭킹 보드는 특수 랭킹 유지
        ...(b.slug === "ranking"
          ? (mainSpecial ? { mainPageViewTypeId: mainSpecial.id } : {})
          : (mainText ? { mainPageViewTypeId: mainText.id } : {})),
        ...(b.slug === "ranking" && listSpecial ? { listViewTypeId: listSpecial.id } : {}),
      },
    });
    created.push(board);

    // 공지/운영 보드는 관리자 모더레이터 지정
    if (["notice", "bug-report"].includes(board.slug)) {
      await prisma.boardModerator.upsert({
        where: {
          boardId_userId: { boardId: board.id, userId: admin.userId },
        },
        update: {},
        create: { boardId: board.id, userId: admin.userId, role: "MANAGER" },
      });
    }
  }
  return created;
}

async function upsertViewTypes() {
  const viewTypes = [
    // main scope
    { key: "main_default", name: "기본", scope: "main" },
    { key: "main_text", name: "텍스트", scope: "main" },
    { key: "main_preview", name: "미리보기", scope: "main" },
    { key: "main_special_rank", name: "특수랭킹", scope: "main" },
    // list scope
    { key: "list_default", name: "기본", scope: "list" },
    { key: "list_text", name: "텍스트", scope: "list" },
    { key: "list_preview", name: "미리보기", scope: "list" },
    { key: "list_special_rank", name: "특수랭킹", scope: "list" },
  ];
  for (const vt of viewTypes) {
    await prisma.boardViewType.upsert({
      where: { key: vt.key },
      update: { name: vt.name, scope: vt.scope },
      create: vt,
    });
  }
}

async function removeNonPrimaryBoards() {
  // 메인/명예의전당/소통방에 해당하지 않는 게시판(광고/제휴 계열) 정리
  await prisma.board.deleteMany({
    where: { slug: { in: ["partners-photos", "partner-contact", "partner-req"] } },
  });
}

async function createPostsForAllBoards(boards, countPerBoard = 100, admin) {
  const users = await prisma.user.findMany({ select: { userId: true } });
  const userIds = users.map((u) => u.userId);
  for (const board of boards) {
    const data = [];
    for (let i = 0; i < countPerBoard; i++) {
      const authorId = ["notice", "bug-report"].includes(board.slug)
        ? admin.userId
        : userIds[randomInt(0, userIds.length - 1)];
      data.push({
        boardId: board.id,
        authorId,
        title: `${board.name} 샘플 글 ${i + 1}`,
        content: `이 게시판(${board.slug})의 자동 시드 게시물 #${i + 1} 입니다.\n\n테스트용 내용입니다.`,
        status: "published",
        isAnonymous: board.allowAnonymousPost ? Math.random() < 0.3 : false,
      });
    }
    await prisma.post.createMany({ data });
  }
}

async function seedPolicies() {
  // 금칙어 예시
  const banned = [
    { pattern: "광고", appliesTo: "POST", severity: 1 },
    { pattern: "욕설", appliesTo: "COMMENT", severity: 2 },
    { pattern: "스팸", appliesTo: "POST", severity: 2 },
  ];
  for (const k of banned) {
    await prisma.bannedKeyword.upsert({
      where: { pattern: k.pattern },
      update: { appliesTo: k.appliesTo, severity: k.severity, active: true },
      create: k,
    });
  }

  // 레벨 임계 예시
  const levels = [
    { level: 1, minPoints: 0 },
    { level: 2, minPoints: 100 },
    { level: 3, minPoints: 300 },
  ];
  for (const l of levels) {
    await prisma.levelThreshold.upsert({
      where: { level: l.level },
      update: { minPoints: l.minPoints },
      create: l,
    });
  }
}

async function seedPartnerShops() {
  const items = [
    { region: "경기도", name: "test1", address: "수원시 팔달구 매산로 45", imageUrl: "/sample.jpg", sortOrder: 1 },
    { region: "강원도", name: "test2", address: "춘천시 중앙로 112", imageUrl: "/sample.jpg", sortOrder: 2 },
  ];
  for (const it of items) {
    await prisma.partnerShop.upsert({
      where: { name_region: { name: it.name, region: it.region } },
      update: { address: it.address, imageUrl: it.imageUrl, sortOrder: it.sortOrder, active: true },
      create: it,
    });
  }
  // 표시 토글 기본값 보장
  const SETTINGS_KEY = "mainpage_settings";
  const setting = await prisma.setting.findUnique({ where: { key: SETTINGS_KEY } });
  const current = setting ? JSON.parse(setting.value) : {};
  const next = { showPartnerShops: true, ...current };
  await prisma.setting.upsert({
    where: { key: SETTINGS_KEY },
    update: { value: JSON.stringify(next) },
    create: { key: SETTINGS_KEY, value: JSON.stringify(next) },
  });
}

async function seedBanners() {
  // 기존 배너 정리 후 5개만 채움
  await prisma.banner.deleteMany({});
  const items = Array.from({ length: 5 }).map((_, i) => ({
    title: `메인 배너 ${i + 1}`,
    imageUrl: "/sample.jpg",
    linkUrl: "/",
    sortOrder: i + 1,
    active: true,
  }));
  await prisma.banner.createMany({ data: items });
}

async function seedMainpageVisibleBoards(boards) {
  const SETTINGS_KEY = "mainpage_settings";
  const setting = await prisma.setting.findUnique({ where: { key: SETTINGS_KEY } });
  const current = setting ? JSON.parse(setting.value) : {};
  const wantSlugs = new Set(["notice", "free", "ranking"]);
  const visibleBoardIds = boards.filter((b) => wantSlugs.has(b.slug)).map((b) => b.id);
  const next = { ...current, visibleBoardIds };
  await prisma.setting.upsert({
    where: { key: SETTINGS_KEY },
    update: { value: JSON.stringify(next) },
    create: { key: SETTINGS_KEY, value: JSON.stringify(next) },
  });
}

async function main() {
  await upsertRoles();
  const admin = await upsertAdmin();
  const categoryMap = await upsertCategories();
  await upsertViewTypes();
  await createRandomUsers(100);
  await removeNonPrimaryBoards();
  const boards = await upsertBoards(admin, categoryMap);
  await seedMainpageVisibleBoards(boards);
  await createPostsForAllBoards(boards, 100, admin);
  await seedPartnerShops();
  await seedBanners();

  await seedPolicies();

  // 제휴업체 예시 데이터
  const partners = [
    { name: "test", category: "spa", latitude: 37.5700, longitude: 126.9769, address: "서울 종로구" },
    { name: "굿짐", category: "gym", latitude: 37.5600, longitude: 126.9820, address: "서울 중구" },
  ];
  for (const p of partners) {
    await prisma.partner.upsert({ where: { name: p.name }, update: p, create: p });
  }
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });


