import { wingPool } from '../db/wingDb.js';
import { AuthError } from '../auth/authService.js';

// ============================================================
// 인터페이스
// ============================================================

interface TemplateSectionDef {
  sectCd: string;
  sectNm: string;
  fieldDef: unknown[];
  sortOrd: number;
}

interface TemplateItem {
  tmplSn: number;
  tmplCd: string;
  tmplNm: string;
  icon: string;
  tmplDc: string;
  sections: TemplateSectionDef[];
}

interface CategoryTemplateDef {
  icon: string;
  label: string;
}

interface CategorySectionDef {
  sectCd: string;
  sectNm: string;
  icon: string;
  sectDc: string;
  dfltYn: string;
  sortOrd: number;
}

interface CategoryItem {
  ctgrSn: number;
  ctgrCd: string;
  ctgrNm: string;
  icon: string;
  ctgrDc: string;
  colorCd: string;
  borderColor: string;
  bgActive: string;
  reportNm: string;
  templates: CategoryTemplateDef[];
  sections: CategorySectionDef[];
}

interface ReportListItem {
  reportSn: number;
  tmplCd: string | null;
  tmplNm: string | null;
  ctgrCd: string | null;
  ctgrNm: string | null;
  title: string;
  jrsdCd: string | null;
  sttsCd: string;
  authorId: string;
  authorName: string;
  acdntSn: number | null;
  regDtm: string;
  mdfcnDtm: string | null;
  hasMapCapture: boolean;
}

interface SectionData {
  sectCd: string;
  includeYn: string;
  sectData: unknown;
  sortOrd: number;
}

interface ReportDetail extends ReportListItem {
  acdntSn: number | null;
  sections: SectionData[];
  step3MapImg: string | null;
  step6MapImg: string | null;
}

interface ListReportsInput {
  jrsdCd?: string;
  tmplCd?: string;
  sttsCd?: string;
  search?: string;
  page?: number;
  size?: number;
}

interface ListReportsResult {
  items: ReportListItem[];
  totalCount: number;
  page: number;
  size: number;
}

interface CreateReportInput {
  tmplSn?: number;
  ctgrSn?: number;
  acdntSn?: number;
  title: string;
  jrsdCd?: string;
  sttsCd?: string;
  authorId: string;
  step3MapImg?: string;
  step6MapImg?: string;
  sections?: { sectCd: string; includeYn?: string; sectData: unknown; sortOrd?: number }[];
}

interface UpdateReportInput {
  title?: string;
  jrsdCd?: string;
  sttsCd?: string;
  acdntSn?: number | null;
  step3MapImg?: string | null;
  step6MapImg?: string | null;
  sections?: { sectCd: string; includeYn?: string; sectData: unknown; sortOrd?: number }[];
}

// ============================================================
// 템플릿/카테고리 조회
// ============================================================

export async function getTemplates(): Promise<TemplateItem[]> {
  const tmplRes = await wingPool.query(
    `SELECT TMPL_SN, TMPL_CD, TMPL_NM, ICON, TMPL_DC
     FROM REPORT_TMPL WHERE USE_YN = 'Y' ORDER BY SORT_ORD`
  );

  const sectRes = await wingPool.query(
    `SELECT s.TMPL_SN, s.SECT_CD, s.SECT_NM, s.FIELD_DEF, s.SORT_ORD
     FROM REPORT_TMPL_SECT s
     JOIN REPORT_TMPL t ON t.TMPL_SN = s.TMPL_SN
     WHERE s.USE_YN = 'Y' AND t.USE_YN = 'Y'
     ORDER BY s.TMPL_SN, s.SORT_ORD`
  );

  const sectMap = new Map<number, TemplateSectionDef[]>();
  for (const row of sectRes.rows) {
    const sn = row.tmpl_sn;
    if (!sectMap.has(sn)) sectMap.set(sn, []);
    sectMap.get(sn)!.push({
      sectCd: row.sect_cd,
      sectNm: row.sect_nm,
      fieldDef: row.field_def,
      sortOrd: row.sort_ord,
    });
  }

  return tmplRes.rows.map((r) => ({
    tmplSn: r.tmpl_sn,
    tmplCd: r.tmpl_cd,
    tmplNm: r.tmpl_nm,
    icon: r.icon || '',
    tmplDc: r.tmpl_dc || '',
    sections: sectMap.get(r.tmpl_sn) || [],
  }));
}

export async function getCategories(): Promise<CategoryItem[]> {
  const ctgrRes = await wingPool.query(
    `SELECT CTGR_SN, CTGR_CD, CTGR_NM, ICON, CTGR_DC,
            COLOR_CD, BORDER_COLOR, BG_ACTIVE, REPORT_NM
     FROM REPORT_ANALYSIS_CTGR WHERE USE_YN = 'Y' ORDER BY SORT_ORD`
  );

  const tmplRes = await wingPool.query(
    `SELECT ct.CTGR_SN, ct.ICON, ct.LABEL
     FROM REPORT_CTGR_TMPL ct
     JOIN REPORT_ANALYSIS_CTGR c ON c.CTGR_SN = ct.CTGR_SN
     WHERE c.USE_YN = 'Y'
     ORDER BY ct.CTGR_SN, ct.SORT_ORD`
  );

  const sectRes = await wingPool.query(
    `SELECT cs.CTGR_SN, cs.SECT_CD, cs.SECT_NM, cs.ICON, cs.SECT_DC, cs.DFLT_YN, cs.SORT_ORD
     FROM REPORT_CTGR_SECT cs
     JOIN REPORT_ANALYSIS_CTGR c ON c.CTGR_SN = cs.CTGR_SN
     WHERE c.USE_YN = 'Y'
     ORDER BY cs.CTGR_SN, cs.SORT_ORD`
  );

  const tmplMap = new Map<number, CategoryTemplateDef[]>();
  for (const row of tmplRes.rows) {
    const sn = row.ctgr_sn;
    if (!tmplMap.has(sn)) tmplMap.set(sn, []);
    tmplMap.get(sn)!.push({ icon: row.icon || '', label: row.label });
  }

  const sectMap = new Map<number, CategorySectionDef[]>();
  for (const row of sectRes.rows) {
    const sn = row.ctgr_sn;
    if (!sectMap.has(sn)) sectMap.set(sn, []);
    sectMap.get(sn)!.push({
      sectCd: row.sect_cd,
      sectNm: row.sect_nm,
      icon: row.icon || '',
      sectDc: row.sect_dc || '',
      dfltYn: row.dflt_yn,
      sortOrd: row.sort_ord,
    });
  }

  return ctgrRes.rows.map((r) => ({
    ctgrSn: r.ctgr_sn,
    ctgrCd: r.ctgr_cd,
    ctgrNm: r.ctgr_nm,
    icon: r.icon || '',
    ctgrDc: r.ctgr_dc || '',
    colorCd: r.color_cd || '',
    borderColor: r.border_color || '',
    bgActive: r.bg_active || '',
    reportNm: r.report_nm || '',
    templates: tmplMap.get(r.ctgr_sn) || [],
    sections: sectMap.get(r.ctgr_sn) || [],
  }));
}

// ============================================================
// 보고서 CRUD
// ============================================================

export async function listReports(input: ListReportsInput): Promise<ListReportsResult> {
  const page = Math.max(1, input.page || 1);
  const size = Math.min(100, Math.max(1, input.size || 20));
  const offset = (page - 1) * size;

  const conditions: string[] = ["r.USE_YN = 'Y'"];
  const params: unknown[] = [];
  let idx = 1;

  if (input.jrsdCd) {
    conditions.push(`r.JRSD_CD = $${idx++}`);
    params.push(input.jrsdCd);
  }
  if (input.tmplCd) {
    conditions.push(`t.TMPL_CD = $${idx++}`);
    params.push(input.tmplCd);
  }
  if (input.sttsCd) {
    conditions.push(`r.STTS_CD = $${idx++}`);
    params.push(input.sttsCd);
  }
  if (input.search) {
    conditions.push(`r.TITLE ILIKE $${idx++}`);
    params.push(`%${input.search}%`);
  }

  const where = conditions.join(' AND ');

  const countRes = await wingPool.query(
    `SELECT COUNT(*) AS cnt
     FROM REPORT r
     LEFT JOIN REPORT_TMPL t ON t.TMPL_SN = r.TMPL_SN
     WHERE ${where}`,
    params
  );
  const totalCount = parseInt(countRes.rows[0].cnt, 10);

  const dataRes = await wingPool.query(
    `SELECT r.REPORT_SN, t.TMPL_CD, t.TMPL_NM,
            c.CTGR_CD, c.CTGR_NM,
            r.TITLE, r.JRSD_CD, r.STTS_CD,
            r.AUTHOR_ID, u.USER_NM AS AUTHOR_NAME,
            r.ACDNT_SN, r.REG_DTM, r.MDFCN_DTM,
            CASE WHEN (r.STEP3_MAP_IMG IS NOT NULL AND r.STEP3_MAP_IMG <> '')
                   OR (r.STEP6_MAP_IMG IS NOT NULL AND r.STEP6_MAP_IMG <> '')
                 THEN true ELSE false END AS HAS_MAP_CAPTURE
     FROM REPORT r
     LEFT JOIN REPORT_TMPL t ON t.TMPL_SN = r.TMPL_SN
     LEFT JOIN REPORT_ANALYSIS_CTGR c ON c.CTGR_SN = r.CTGR_SN
     LEFT JOIN AUTH_USER u ON u.USER_ID = r.AUTHOR_ID
     WHERE ${where}
     ORDER BY r.REG_DTM DESC
     LIMIT $${idx++} OFFSET $${idx++}`,
    [...params, size, offset]
  );

  return {
    items: dataRes.rows.map((r) => ({
      reportSn: r.report_sn,
      tmplCd: r.tmpl_cd,
      tmplNm: r.tmpl_nm,
      ctgrCd: r.ctgr_cd,
      ctgrNm: r.ctgr_nm,
      title: r.title,
      jrsdCd: r.jrsd_cd,
      sttsCd: r.stts_cd,
      authorId: r.author_id,
      authorName: r.author_name || '',
      acdntSn: r.acdnt_sn,
      regDtm: r.reg_dtm,
      mdfcnDtm: r.mdfcn_dtm,
      hasMapCapture: r.has_map_capture,
    })),
    totalCount,
    page,
    size,
  };
}

export async function getReport(reportSn: number): Promise<ReportDetail> {
  const res = await wingPool.query(
    `SELECT r.REPORT_SN, t.TMPL_CD, t.TMPL_NM,
            c.CTGR_CD, c.CTGR_NM,
            r.TITLE, r.JRSD_CD, r.STTS_CD, r.ACDNT_SN,
            r.AUTHOR_ID, u.USER_NM AS AUTHOR_NAME,
            r.REG_DTM, r.MDFCN_DTM, r.STEP3_MAP_IMG, r.STEP6_MAP_IMG,
            CASE WHEN (r.STEP3_MAP_IMG IS NOT NULL AND r.STEP3_MAP_IMG <> '')
                   OR (r.STEP6_MAP_IMG IS NOT NULL AND r.STEP6_MAP_IMG <> '')
                 THEN true ELSE false END AS HAS_MAP_CAPTURE
     FROM REPORT r
     LEFT JOIN REPORT_TMPL t ON t.TMPL_SN = r.TMPL_SN
     LEFT JOIN REPORT_ANALYSIS_CTGR c ON c.CTGR_SN = r.CTGR_SN
     LEFT JOIN AUTH_USER u ON u.USER_ID = r.AUTHOR_ID
     WHERE r.REPORT_SN = $1 AND r.USE_YN = 'Y'`,
    [reportSn]
  );

  if (res.rows.length === 0) {
    throw new AuthError('보고서를 찾을 수 없습니다.', 404);
  }

  const r = res.rows[0];

  const sectRes = await wingPool.query(
    `SELECT SECT_CD, INCLUDE_YN, SECT_DATA, SORT_ORD
     FROM REPORT_SECT_DATA
     WHERE REPORT_SN = $1
     ORDER BY SORT_ORD`,
    [reportSn]
  );

  return {
    reportSn: r.report_sn,
    tmplCd: r.tmpl_cd,
    tmplNm: r.tmpl_nm,
    ctgrCd: r.ctgr_cd,
    ctgrNm: r.ctgr_nm,
    title: r.title,
    jrsdCd: r.jrsd_cd,
    sttsCd: r.stts_cd,
    acdntSn: r.acdnt_sn,
    authorId: r.author_id,
    authorName: r.author_name || '',
    regDtm: r.reg_dtm,
    mdfcnDtm: r.mdfcn_dtm,
    step3MapImg: r.step3_map_img,
    step6MapImg: r.step6_map_img,
    hasMapCapture: r.has_map_capture,
    sections: sectRes.rows.map((s) => ({
      sectCd: s.sect_cd,
      includeYn: s.include_yn,
      sectData: s.sect_data,
      sortOrd: s.sort_ord,
    })),
  };
}

export async function createReport(input: CreateReportInput): Promise<{ sn: number }> {
  if (!input.title?.trim()) {
    throw new AuthError('보고서 제목은 필수입니다.', 400);
  }

  const client = await wingPool.connect();
  try {
    await client.query('BEGIN');

    const res = await client.query(
      `INSERT INTO REPORT (TMPL_SN, CTGR_SN, ACDNT_SN, TITLE, JRSD_CD, STTS_CD, AUTHOR_ID, STEP3_MAP_IMG, STEP6_MAP_IMG)
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
       RETURNING REPORT_SN`,
      [
        input.tmplSn || null,
        input.ctgrSn || null,
        input.acdntSn || null,
        input.title.trim(),
        input.jrsdCd || null,
        input.sttsCd || 'DRAFT',
        input.authorId,
        input.step3MapImg || null,
        input.step6MapImg || null,
      ]
    );
    const reportSn = res.rows[0].report_sn;

    if (input.sections && input.sections.length > 0) {
      for (const sect of input.sections) {
        await client.query(
          `INSERT INTO REPORT_SECT_DATA (REPORT_SN, SECT_CD, INCLUDE_YN, SECT_DATA, SORT_ORD)
           VALUES ($1, $2, $3, $4, $5)`,
          [
            reportSn,
            sect.sectCd,
            sect.includeYn || 'Y',
            JSON.stringify(sect.sectData || {}),
            sect.sortOrd || 0,
          ]
        );
      }
    }

    await client.query('COMMIT');
    return { sn: reportSn };
  } catch (err) {
    await client.query('ROLLBACK');
    throw err;
  } finally {
    client.release();
  }
}

export async function updateReport(
  reportSn: number,
  input: UpdateReportInput,
  requesterId: string
): Promise<void> {
  const existing = await wingPool.query(
    `SELECT AUTHOR_ID FROM REPORT WHERE REPORT_SN = $1 AND USE_YN = 'Y'`,
    [reportSn]
  );
  if (existing.rows.length === 0) {
    throw new AuthError('보고서를 찾을 수 없습니다.', 404);
  }
  if (existing.rows[0].author_id !== requesterId) {
    throw new AuthError('본인의 보고서만 수정할 수 있습니다.', 403);
  }

  const client = await wingPool.connect();
  try {
    await client.query('BEGIN');

    // 메타데이터 업데이트
    const sets: string[] = ['MDFCN_DTM = NOW()'];
    const params: unknown[] = [];
    let idx = 1;

    if (input.title !== undefined) {
      sets.push(`TITLE = $${idx++}`);
      params.push(input.title.trim());
    }
    if (input.jrsdCd !== undefined) {
      sets.push(`JRSD_CD = $${idx++}`);
      params.push(input.jrsdCd);
    }
    if (input.sttsCd !== undefined) {
      sets.push(`STTS_CD = $${idx++}`);
      params.push(input.sttsCd);
    }
    if (input.acdntSn !== undefined) {
      sets.push(`ACDNT_SN = $${idx++}`);
      params.push(input.acdntSn);
    }
    if (input.step3MapImg !== undefined) {
      sets.push(`STEP3_MAP_IMG = $${idx++}`);
      params.push(input.step3MapImg);
    }
    if (input.step6MapImg !== undefined) {
      sets.push(`STEP6_MAP_IMG = $${idx++}`);
      params.push(input.step6MapImg);
    }

    params.push(reportSn);
    await client.query(
      `UPDATE REPORT SET ${sets.join(', ')} WHERE REPORT_SN = $${idx}`,
      params
    );

    // 섹션 데이터 upsert
    if (input.sections && input.sections.length > 0) {
      for (const sect of input.sections) {
        await client.query(
          `INSERT INTO REPORT_SECT_DATA (REPORT_SN, SECT_CD, INCLUDE_YN, SECT_DATA, SORT_ORD)
           VALUES ($1, $2, $3, $4, $5)
           ON CONFLICT (REPORT_SN, SECT_CD) DO UPDATE
           SET INCLUDE_YN = EXCLUDED.INCLUDE_YN,
               SECT_DATA = EXCLUDED.SECT_DATA,
               SORT_ORD = EXCLUDED.SORT_ORD`,
          [
            reportSn,
            sect.sectCd,
            sect.includeYn || 'Y',
            JSON.stringify(sect.sectData || {}),
            sect.sortOrd || 0,
          ]
        );
      }
    }

    await client.query('COMMIT');
  } catch (err) {
    await client.query('ROLLBACK');
    throw err;
  } finally {
    client.release();
  }
}

export async function deleteReport(reportSn: number, requesterId: string): Promise<void> {
  const existing = await wingPool.query(
    `SELECT AUTHOR_ID FROM REPORT WHERE REPORT_SN = $1 AND USE_YN = 'Y'`,
    [reportSn]
  );
  if (existing.rows.length === 0) {
    throw new AuthError('보고서를 찾을 수 없습니다.', 404);
  }
  if (existing.rows[0].author_id !== requesterId) {
    throw new AuthError('본인의 보고서만 삭제할 수 있습니다.', 403);
  }

  await wingPool.query(
    `UPDATE REPORT SET USE_YN = 'N', MDFCN_DTM = NOW() WHERE REPORT_SN = $1`,
    [reportSn]
  );
}

export async function updateReportSection(
  reportSn: number,
  sectCd: string,
  sectData: unknown,
  includeYn: string,
  requesterId: string
): Promise<void> {
  const existing = await wingPool.query(
    `SELECT AUTHOR_ID FROM REPORT WHERE REPORT_SN = $1 AND USE_YN = 'Y'`,
    [reportSn]
  );
  if (existing.rows.length === 0) {
    throw new AuthError('보고서를 찾을 수 없습니다.', 404);
  }
  if (existing.rows[0].author_id !== requesterId) {
    throw new AuthError('본인의 보고서만 수정할 수 있습니다.', 403);
  }

  await wingPool.query(
    `INSERT INTO REPORT_SECT_DATA (REPORT_SN, SECT_CD, INCLUDE_YN, SECT_DATA)
     VALUES ($1, $2, $3, $4)
     ON CONFLICT (REPORT_SN, SECT_CD) DO UPDATE
     SET INCLUDE_YN = EXCLUDED.INCLUDE_YN,
         SECT_DATA = EXCLUDED.SECT_DATA`,
    [reportSn, sectCd, includeYn || 'Y', JSON.stringify(sectData || {})]
  );

  await wingPool.query(
    `UPDATE REPORT SET MDFCN_DTM = NOW() WHERE REPORT_SN = $1`,
    [reportSn]
  );
}
