/**
 * 프롬프트 라이브러리(공식 JSON + DB 커뮤니티/좋아요)
 */
"use strict";

const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

function maskEmailForDisplay(email) {
  const s = String(email || "")
    .trim()
    .toLowerCase();
  const at = s.indexOf("@");
  if (at < 1) return "팀원";
  const local = s.slice(0, at);
  const dom = s.slice(at);
  if (local.length <= 2) {
    return `${local[0] || "*"}*${dom}`;
  }
  return `${local[0]}**${local.slice(-1)}${dom}`;
}

/** @ 사인 앞 로컬부(예: spark_ai@ncue.net → spark_ai) — 라이브러리 카드·메타 표시용 */
function emailLocalPartForDisplay(email) {
  const s = String(email || "").trim();
  if (!s) return "팀원";
  const at = s.indexOf("@");
  if (at < 1) {
    const t = s.slice(0, 80);
    return t || "팀원";
  }
  const local = s.slice(0, at);
  return (local && local.trim()) || "팀원";
}

/**
 * @param {import("pg").Pool} pool
 * @param {Array<{id:string,title?:string,description?:string,tag?:string,body?:string}>} officialPrompts
 * @param {string | null} userEmail
 */
async function getLibraryData(pool, officialPrompts, userEmail) {
  const official = Array.isArray(officialPrompts) ? officialPrompts : [];
  const officialIds = official.map((p) => p.id).filter((id) => id && String(id).length < 200);

  if (!pool) {
    return {
      hasDb: false,
      community: [],
      likeCountOfficial: {},
      likeCountCommunity: {},
      myOfficialLikes: [],
      myCommunityLikes: [],
      mySubmissions: [],
    };
  }

  const [commRes, offCntRes, myLikesRes] = await Promise.all([
    pool.query(
      `SELECT id, author_email, title, description, body, tag, created_at, prompt_attachments, result_sample_attachments
       FROM prompt_community_entries
       WHERE is_published = true AND is_deleted = false
       ORDER BY created_at DESC
       LIMIT 300`
    ),
    officialIds.length
      ? pool.query(
          `SELECT target_id, COUNT(*)::int AS c
           FROM prompt_likes
           WHERE target_kind = 'official' AND target_id = ANY($1::text[])
           GROUP BY target_id`,
          [officialIds]
        )
      : Promise.resolve({ rows: [] }),
    userEmail
      ? pool.query(
          `SELECT target_kind, target_id FROM prompt_likes WHERE user_email = $1`,
          [userEmail]
        )
      : Promise.resolve({ rows: [] }),
  ]);

  const commIds = commRes.rows.map((r) => r.id);
  let commCntRes = { rows: [] };
  if (commIds.length) {
    commCntRes = await pool.query(
      `SELECT target_id, COUNT(*)::int AS c
       FROM prompt_likes
       WHERE target_kind = 'community' AND target_id = ANY($1::text[])
       GROUP BY target_id`,
      [commIds.map((id) => String(id))]
    );
  }

  const likeCountOfficial = {};
  for (const r of offCntRes.rows) {
    likeCountOfficial[r.target_id] = r.c;
  }
  const likeCountCommunity = {};
  for (const r of commCntRes.rows) {
    likeCountCommunity[r.target_id] = r.c;
  }

  const myOfficialLikes = [];
  const myCommunityLikes = [];
  for (const r of myLikesRes.rows) {
    if (r.target_kind === "official") myOfficialLikes.push(r.target_id);
    else if (r.target_kind === "community") myCommunityLikes.push(r.target_id);
  }

  let mySubmissions = [];
  if (userEmail) {
    const mine = await pool.query(
      `SELECT id, title, created_at
       FROM prompt_community_entries
       WHERE author_email = $1 AND is_deleted = false
       ORDER BY created_at DESC
       LIMIT 100`,
      [userEmail]
    );
    mySubmissions = (mine.rows || []).map((row) => ({
      id: String(row.id),
      title: (row.title || "").trim() || "제목 없음",
      createdAt: row.created_at ? new Date(row.created_at).toISOString() : "",
    }));
  }

  const community = (commRes.rows || []).map((row) => {
    const parseFileJson = (raw) => {
      try {
        const j = typeof raw === "string" ? JSON.parse(raw) : raw;
        return Array.isArray(j) ? j : [];
      } catch {
        return [];
      }
    };
    return {
    id: String(row.id),
    title: (row.title || "").trim() || "제목 없음",
    description: String(row.description || "").trim(),
    body: String(row.body || ""),
    tag: (row.tag || "기타").trim() || "기타",
    authorLabel: emailLocalPartForDisplay(row.author_email),
    likeCount: likeCountCommunity[String(row.id)] || 0,
    createdAt: row.created_at ? new Date(row.created_at).toISOString() : "",
    promptFiles: parseFileJson(row.prompt_attachments).filter((f) => f && f.relativePath),
    resultFiles: parseFileJson(row.result_sample_attachments).filter((f) => f && f.relativePath),
    };
  });

  return {
    hasDb: true,
    community,
    likeCountOfficial,
    likeCountCommunity,
    myOfficialLikes,
    myCommunityLikes,
    mySubmissions,
  };
}

/**
 * @param {import("pg").Pool} pool
 * @param {string} userEmail
 * @param {string} kind
 * @param {string} targetId
 * @param {Set<string>} officialIdSet
 */
async function toggleLike(pool, userEmail, kind, targetId, officialIdSet) {
  if (!["official", "community"].includes(kind)) {
    const err = new Error("target_kind");
    err.code = "VALIDATION";
    throw err;
  }
  const id = String(targetId || "").trim();
  if (!id) {
    const err = new Error("target_id");
    err.code = "VALIDATION";
    throw err;
  }
  if (kind === "official" && !officialIdSet.has(id)) {
    const err = new Error("알 수 없는 공식 프롬프트입니다.");
    err.code = "VALIDATION";
    throw err;
  }
  if (kind === "community" && !UUID_RE.test(id)) {
    const err = new Error("잘못된 ID입니다.");
    err.code = "VALIDATION";
    throw err;
  }
  if (kind === "community") {
    const r = await pool.query(
      `SELECT 1 FROM prompt_community_entries WHERE id = $1::uuid AND is_deleted = false AND is_published = true LIMIT 1`,
      [id]
    );
    if (!r.rowCount) {
      const err = new Error("삭제되었거나 없는 프롬프트입니다.");
      err.code = "NOT_FOUND";
      throw err;
    }
  }

  const del = await pool.query(
    `DELETE FROM prompt_likes
     WHERE user_email = $1 AND target_kind = $2 AND target_id = $3
     RETURNING id`,
    [userEmail, kind, id]
  );
  if (del.rowCount) {
    const cnt = await pool.query(
      `SELECT COUNT(*)::int AS c FROM prompt_likes WHERE target_kind = $1 AND target_id = $2`,
      [kind, id]
    );
    return { liked: false, likeCount: cnt.rows[0].c || 0 };
  }
  await pool.query(
    `INSERT INTO prompt_likes (user_email, target_kind, target_id) VALUES ($1, $2, $3)`,
    [userEmail, kind, id]
  );
  const cnt = await pool.query(
    `SELECT COUNT(*)::int AS c FROM prompt_likes WHERE target_kind = $1 AND target_id = $2`,
    [kind, id]
  );
  return { liked: true, likeCount: cnt.rows[0].c || 0 };
}

const MAX_TITLE = 500;
const MAX_DESC = 2000;
const MAX_BODY = 50000;
const MAX_TAG = 100;
const MAX_ATTACH_PER_GROUP = 5;
const MAX_ATTACH_BYTES = 20 * 1024 * 1024;

/**
 * @param {import("pg").Pool} pool
 * @param {object} p
 * @param {string} p.authorEmail
 * @param {string} p.title
 * @param {string} p.description
 * @param {string} p.body
 * @param {string} p.tag
 * @param {Array<{ originalName: string, relativePath: string, size: number }>} [p.promptAttachments]
 * @param {Array<{ originalName: string, relativePath: string, size: number }>} [p.resultSampleAttachments]
 */
function normalizeFileList(arr, maxN) {
  if (!Array.isArray(arr) || !arr.length) return [];
  return arr
    .filter((a) => a && typeof a.relativePath === "string" && a.relativePath.startsWith("/uploads/"))
    .slice(0, maxN)
    .map((a) => ({
      originalName: String(a.originalName || "file").slice(0, 400),
      relativePath: String(a.relativePath).slice(0, 2000),
      size: Math.min(Number(a.size) || 0, MAX_ATTACH_BYTES * 2),
    }));
}

async function createCommunityEntry(pool, p) {
  const title = String(p.title || "")
    .trim()
    .slice(0, MAX_TITLE);
  const description = String(p.description || "")
    .trim()
    .slice(0, MAX_DESC);
  const body = String(p.body || "").trim();
  const tag = String(p.tag || "기타")
    .trim()
    .slice(0, MAX_TAG) || "기타";
  if (!title) {
    const e = new Error("제목을 입력해 주세요.");
    e.code = "VALIDATION";
    throw e;
  }
  if (body.length < 10) {
    const e = new Error("본문은 10자 이상 입력해 주세요.");
    e.code = "VALIDATION";
    throw e;
  }
  if (body.length > MAX_BODY) {
    const e = new Error(`본문은 ${MAX_BODY}자 이하여야 합니다.`);
    e.code = "VALIDATION";
    throw e;
  }
  const promptA = normalizeFileList(p.promptAttachments, MAX_ATTACH_PER_GROUP);
  const resultA = normalizeFileList(p.resultSampleAttachments, MAX_ATTACH_PER_GROUP);
  const r = await pool.query(
    `INSERT INTO prompt_community_entries (author_email, title, description, body, tag, prompt_attachments, result_sample_attachments)
     VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7::jsonb)
     RETURNING id, created_at`,
    [p.authorEmail, title, description, body, tag, JSON.stringify(promptA), JSON.stringify(resultA)]
  );
  const row = r.rows[0];
  return {
    id: String(row.id),
    createdAt: row.created_at ? new Date(row.created_at).toISOString() : "",
    promptFiles: promptA,
    resultFiles: resultA,
  };
}

/**
 * @param {import("pg").Pool} pool
 * @param {string} id
 * @param {string} authorEmail
 */
async function softDeleteCommunityEntry(pool, id, authorEmail) {
  if (!UUID_RE.test(String(id || "").trim())) {
    const e = new Error("잘못된 ID입니다.");
    e.code = "VALIDATION";
    throw e;
  }
  const r = await pool.query(
    `UPDATE prompt_community_entries
     SET is_deleted = true, is_published = false, updated_at = NOW()
     WHERE id = $1::uuid AND author_email = $2 AND is_deleted = false
     RETURNING id`,
    [id, authorEmail]
  );
  if (!r.rowCount) {
    const e = new Error("삭제할 수 없습니다. 본인이 올린 글만 삭제할 수 있습니다.");
    e.code = "NOT_FOUND";
    throw e;
  }
  return { ok: true };
}

module.exports = {
  getLibraryData,
  toggleLike,
  createCommunityEntry,
  softDeleteCommunityEntry,
  maskEmailForDisplay,
  emailLocalPartForDisplay,
  UUID_RE,
  MAX_ATTACH_PER_GROUP,
  MAX_ATTACH_BYTES,
};
