/**
 * 회의록 AI: PostgreSQL 또는 data/meeting-ai.json 폴백
 */
const path = require("path");
const fs = require("fs/promises");
const { v4: uuidv4 } = require("uuid");
const { extractMeetingSummary } = require("./meeting-minutes-summary");
const { formatMeetingDateOnly } = require("./meeting-date-format");
const taskChecklistStore = require("./task-checklist-store");

const MEETING_AI_FILE = path.join(__dirname, "..", "data", "meeting-ai.json");

const defaultPromptRow = (email) => ({
  user_email: email,
  include_title_line: true,
  include_attendees: true,
  include_summary: true,
  include_action_items: true,
  include_checklist: false,
  custom_instructions: null,
});

let writeChain = Promise.resolve();

function withFileLock(fn) {
  const p = writeChain.then(() => fn());
  writeChain = p.catch(() => {});
  return p;
}

async function readFileStore() {
  try {
    const raw = await fs.readFile(MEETING_AI_FILE, "utf8");
    const j = JSON.parse(raw);
    if (!j || typeof j !== "object") return { prompts: {}, meetings: [] };
    if (!j.prompts || typeof j.prompts !== "object") j.prompts = {};
    if (!Array.isArray(j.meetings)) j.meetings = [];
    return j;
  } catch {
    return { prompts: {}, meetings: [] };
  }
}

async function writeFileStore(data) {
  const dir = path.dirname(MEETING_AI_FILE);
  await fs.mkdir(dir, { recursive: true });
  const tmp = `${MEETING_AI_FILE}.${process.pid}.tmp`;
  await fs.writeFile(tmp, JSON.stringify(data, null, 2), "utf8");
  await fs.rename(tmp, MEETING_AI_FILE);
}

function nowIso() {
  return new Date().toISOString();
}

function rowToMeeting(row) {
  return {
    ...row,
    created_at: row.created_at || nowIso(),
    updated_at: row.updated_at || nowIso(),
  };
}

/**
 * @param {import("pg").Pool | null} pgPool
 * @param {string} email
 */
async function ensureUserAndDefaultPrompt(pgPool, email) {
  if (pgPool) {
    await pgPool.query(`INSERT INTO meeting_ai_users (email) VALUES ($1) ON CONFLICT (email) DO NOTHING`, [email]);
    await pgPool.query(
      `INSERT INTO meeting_ai_prompts (user_email, include_title_line, include_attendees, include_summary, include_action_items, include_checklist)
       VALUES ($1, true, true, true, true, false)
       ON CONFLICT (user_email) DO NOTHING`,
      [email]
    );
    return;
  }
  await withFileLock(async () => {
    const data = await readFileStore();
    if (!data.prompts[email]) {
      data.prompts[email] = defaultPromptRow(email);
      await writeFileStore(data);
    }
  });
}

/**
 * @param {import("pg").Pool | null} pgPool
 * @param {string} email
 */
async function getPromptRow(pgPool, email) {
  if (pgPool) {
    const r = await pgPool.query(`SELECT * FROM meeting_ai_prompts WHERE user_email = $1`, [email]);
    return r.rows?.[0] || null;
  }
  const data = await readFileStore();
  const p = data.prompts[email];
  if (!p) return null;
  return {
    id: p.id || null,
    user_email: email,
    include_title_line: p.include_title_line !== false,
    include_attendees: p.include_attendees !== false,
    include_summary: p.include_summary !== false,
    include_action_items: p.include_action_items !== false,
    include_checklist: p.include_checklist === true,
    custom_instructions: p.custom_instructions,
    created_at: p.created_at || null,
    updated_at: p.updated_at || null,
  };
}

/**
 * @param {import("pg").Pool | null} pgPool
 */
async function upsertPrompt(pgPool, email, fields) {
  if (pgPool) {
    await pgPool.query(
      `INSERT INTO meeting_ai_prompts (
        user_email, include_title_line, include_attendees, include_summary, include_action_items, include_checklist, custom_instructions
      ) VALUES ($1, $2, $3, $4, $5, $6, $7)
      ON CONFLICT (user_email) DO UPDATE SET
        include_title_line = EXCLUDED.include_title_line,
        include_attendees = EXCLUDED.include_attendees,
        include_summary = EXCLUDED.include_summary,
        include_action_items = EXCLUDED.include_action_items,
        include_checklist = EXCLUDED.include_checklist,
        custom_instructions = EXCLUDED.custom_instructions,
        updated_at = NOW()`,
      [
        email,
        fields.includeTitleLine,
        fields.includeAttendees,
        fields.includeSummary,
        fields.includeActionItems,
        fields.includeChecklist === true,
        fields.customInstructions || null,
      ]
    );
    const r = await pgPool.query(`SELECT * FROM meeting_ai_prompts WHERE user_email = $1`, [email]);
    return r.rows?.[0] || null;
  }
  return withFileLock(async () => {
    const data = await readFileStore();
    const t = nowIso();
    const prev = data.prompts[email] || defaultPromptRow(email);
    data.prompts[email] = {
      ...prev,
      include_title_line: fields.includeTitleLine,
      include_attendees: fields.includeAttendees,
      include_summary: fields.includeSummary,
      include_action_items: fields.includeActionItems,
      include_checklist: fields.includeChecklist === true,
      custom_instructions: fields.customInstructions || null,
      updated_at: t,
      created_at: prev.created_at || t,
    };
    await writeFileStore(data);
    return await getPromptRow(null, email);
  });
}

/**
 * @param {import("pg").Pool | null} pgPool
 */
async function listMeetings(pgPool, email) {
  if (pgPool) {
    const r = await pgPool.query(
      `SELECT id, user_email, title, source_text, transcript_text, generated_minutes, summary_text, audio_file_path, audio_original_name, chat_model, transcription_model, meeting_date, created_at, updated_at
       FROM meeting_ai_meetings WHERE user_email = $1 ORDER BY created_at DESC LIMIT 200`,
      [email]
    );
    return r.rows || [];
  }
  const data = await readFileStore();
  return data.meetings
    .filter((m) => m.user_email === email)
    .sort((a, b) => new Date(b.created_at) - new Date(a.created_at))
    .slice(0, 200);
}

/**
 * @param {import("pg").Pool | null} pgPool
 */
async function getMeeting(pgPool, id, email) {
  if (pgPool) {
    const r = await pgPool.query(`SELECT * FROM meeting_ai_meetings WHERE id = $1::uuid AND user_email = $2`, [id, email]);
    return r.rows?.[0] || null;
  }
  const data = await readFileStore();
  return data.meetings.find((m) => m.id === id && m.user_email === email) || null;
}

/**
 * @param {import("pg").Pool | null} pgPool
 * @returns {Promise<{ row: object | null, audio_file_path?: string }>}
 */
async function deleteMeeting(pgPool, id, email) {
  await taskChecklistStore.deleteItemsByMeetingId(pgPool, id, email);
  if (pgPool) {
    const prev = await pgPool.query(
      `SELECT audio_file_path FROM meeting_ai_meetings WHERE id = $1::uuid AND user_email = $2`,
      [id, email]
    );
    const row = prev.rows?.[0];
    const del = await pgPool.query(`DELETE FROM meeting_ai_meetings WHERE id = $1::uuid AND user_email = $2 RETURNING id`, [
      id,
      email,
    ]);
    return { deleted: !!del.rowCount, audio_file_path: row?.audio_file_path };
  }
  return withFileLock(async () => {
    const data = await readFileStore();
    const idx = data.meetings.findIndex((m) => m.id === id && m.user_email === email);
    if (idx < 0) return { deleted: false, audio_file_path: null };
    const audio = data.meetings[idx].audio_file_path;
    data.meetings.splice(idx, 1);
    await writeFileStore(data);
    return { deleted: true, audio_file_path: audio };
  });
}

/**
 * @param {import("pg").Pool | null} pgPool
 */
async function insertMeetingText(pgPool, { email, title, sourceText, generated, model, meetingDate }) {
  const summaryText = extractMeetingSummary(generated || "", 1200);
  if (pgPool) {
    const ins = await pgPool.query(
      `INSERT INTO meeting_ai_meetings (user_email, title, source_text, transcript_text, generated_minutes, summary_text, chat_model, meeting_date)
       VALUES ($1, $2, $3, NULL, $4, $5, $6, $7) RETURNING *`,
      [email, title || "제목 없음", sourceText, generated, summaryText || null, model, meetingDate || null]
    );
    return ins.rows?.[0];
  }
  return withFileLock(async () => {
    const data = await readFileStore();
    const t = nowIso();
    const row = rowToMeeting({
      id: uuidv4(),
      user_email: email,
      title: title || "제목 없음",
      source_text: sourceText,
      transcript_text: null,
      generated_minutes: generated,
      summary_text: summaryText || null,
      audio_file_path: null,
      audio_original_name: null,
      chat_model: model,
      transcription_model: null,
      meeting_date: meetingDate || null,
      created_at: t,
      updated_at: t,
    });
    data.meetings.push(row);
    await writeFileStore(data);
    return row;
  });
}

/**
 * @param {import("pg").Pool | null} pgPool
 */
async function insertMeetingAudio(pgPool, { email, title, transcript, generated, relPath, originalName, model, whisperModel, meetingDate }) {
  const summaryText = extractMeetingSummary(generated || "", 1200);
  if (pgPool) {
    const ins = await pgPool.query(
      `INSERT INTO meeting_ai_meetings (user_email, title, source_text, transcript_text, generated_minutes, summary_text, audio_file_path, audio_original_name, chat_model, transcription_model, meeting_date)
         VALUES ($1, $2, NULL, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *`,
      [
        email,
        title || "제목 없음",
        transcript,
        generated,
        summaryText || null,
        relPath,
        (originalName || "").slice(0, 500),
        model,
        whisperModel,
        meetingDate || null,
      ]
    );
    return ins.rows?.[0];
  }
  return withFileLock(async () => {
    const data = await readFileStore();
    const t = nowIso();
    const row = rowToMeeting({
      id: uuidv4(),
      user_email: email,
      title: title || "제목 없음",
      source_text: null,
      transcript_text: transcript,
      generated_minutes: generated,
      summary_text: summaryText || null,
      audio_file_path: relPath,
      audio_original_name: (originalName || "").slice(0, 500),
      chat_model: model,
      transcription_model: whisperModel,
      meeting_date: meetingDate || null,
      created_at: t,
      updated_at: t,
    });
    data.meetings.push(row);
    await writeFileStore(data);
    return row;
  });
}

/**
 * 회의록에 체크리스트 추출 JSON 스냅샷 저장 (업무 체크리스트 자동 연동)
 * @param {import("pg").Pool | null} pgPool
 * @param {string} meetingId
 * @param {string} email
 * @param {object} snapshotObj
 */
function formatMeetingDateIso(d) {
  return formatMeetingDateOnly(d);
}

/**
 * 업무 체크리스트 툴팁용: 회의 제목·일자·요약 (회의 id 목록)
 * @param {import("pg").Pool | null} pgPool
 * @param {string} email
 * @param {string[]} ids
 * @returns {Promise<Map<string, { meetingTitle: string, meetingDate: string|null, meetingSummary: string }>>}
 */
async function getMeetingMetaForIds(pgPool, email, ids) {
  const uid = [...new Set(ids.map((x) => String(x).trim()).filter(Boolean))];
  const map = new Map();
  if (uid.length === 0) return map;

  if (pgPool) {
    const r = await pgPool.query(
      `SELECT id, title, meeting_date, summary_text, generated_minutes FROM meeting_ai_meetings
       WHERE user_email = $1 AND id = ANY($2::uuid[])`,
      [email, uid]
    );
    for (const row of r.rows || []) {
      const id = String(row.id);
      const raw = row.summary_text && String(row.summary_text).trim();
      const summary = raw || extractMeetingSummary(row.generated_minutes || "", 800);
      map.set(id, {
        meetingTitle: (row.title && String(row.title).trim()) || "제목 없음",
        meetingDate: formatMeetingDateIso(row.meeting_date),
        meetingSummary: summary || "",
      });
    }
    return map;
  }

  const data = await readFileStore();
  for (const m of data.meetings || []) {
    if (m.user_email !== email) continue;
    const id = String(m.id);
    if (!uid.includes(id)) continue;
    const raw = m.summary_text && String(m.summary_text).trim();
    const summary = raw || extractMeetingSummary(m.generated_minutes || "", 800);
    map.set(id, {
      meetingTitle: (m.title && String(m.title).trim()) || "제목 없음",
      meetingDate: formatMeetingDateIso(m.meeting_date),
      meetingSummary: summary || "",
    });
  }
  return map;
}

/**
 * 회의록·전사/원문 수동 수정 저장 (요약은 generated_minutes 기준 재계산)
 * @param {import("pg").Pool | null} pgPool
 * @param {string} meetingId
 * @param {string} email
 * @param {{ generatedMinutes: string, transcriptText?: string, sourceText?: string }} fields
 */
async function updateMeetingContent(pgPool, meetingId, email, fields) {
  const gen = (fields.generatedMinutes ?? "").toString();
  const summaryText = extractMeetingSummary(gen, 1200);
  const hasT = Object.prototype.hasOwnProperty.call(fields, "transcriptText");
  const hasS = Object.prototype.hasOwnProperty.call(fields, "sourceText");
  if (pgPool) {
    const params = [meetingId, email, gen, summaryText || null];
    let extra = "";
    if (hasT) {
      params.push(fields.transcriptText ?? "");
      extra += `, transcript_text = $${params.length}`;
    }
    if (hasS) {
      params.push(fields.sourceText ?? "");
      extra += `, source_text = $${params.length}`;
    }
    const r = await pgPool.query(
      `UPDATE meeting_ai_meetings SET generated_minutes = $3, summary_text = $4${extra}, updated_at = NOW()
       WHERE id = $1::uuid AND user_email = $2
       RETURNING *`,
      params
    );
    return r.rows?.[0] || null;
  }
  return withFileLock(async () => {
    const data = await readFileStore();
    const m = data.meetings.find((x) => x.id === meetingId && x.user_email === email);
    if (!m) return null;
    m.generated_minutes = gen;
    m.summary_text = summaryText || null;
    if (hasT) m.transcript_text = fields.transcriptText ?? "";
    if (hasS) m.source_text = fields.sourceText ?? "";
    m.updated_at = nowIso();
    await writeFileStore(data);
    return m;
  });
}

/**
 * @param {import("pg").Pool | null} pgPool
 * @param {string} meetingId
 * @param {string} email
 * @param {string} generatedMinutes
 */
async function updateMeetingGeneratedMinutes(pgPool, meetingId, email, generatedMinutes) {
  return updateMeetingContent(pgPool, meetingId, email, { generatedMinutes });
}

async function updateMeetingChecklistSnapshot(pgPool, meetingId, email, snapshotObj) {
  if (pgPool) {
    await pgPool.query(
      `UPDATE meeting_ai_meetings SET checklist_snapshot = $3::jsonb, updated_at = NOW()
       WHERE id = $1::uuid AND user_email = $2`,
      [meetingId, email, JSON.stringify(snapshotObj)]
    );
    return;
  }
  await withFileLock(async () => {
    const data = await readFileStore();
    const m = data.meetings.find((x) => x.id === meetingId && x.user_email === email);
    if (m) {
      m.checklist_snapshot = snapshotObj;
      m.updated_at = nowIso();
    }
    await writeFileStore(data);
  });
}

module.exports = {
  MEETING_AI_FILE,
  ensureUserAndDefaultPrompt,
  getPromptRow,
  upsertPrompt,
  listMeetings,
  getMeeting,
  deleteMeeting,
  insertMeetingText,
  insertMeetingAudio,
  updateMeetingChecklistSnapshot,
  updateMeetingContent,
  updateMeetingGeneratedMinutes,
  getMeetingMetaForIds,
};
