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

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

interface PostListItem {
  sn: number
  categoryCd: string
  title: string
  authorId: string
  authorName: string
  viewCnt: number
  pinnedYn: string
  regDtm: string
}

interface PostDetail extends PostListItem {
  content: string | null
  mdfcnDtm: string | null
}

interface ListPostsInput {
  categoryCd?: string
  search?: string
  page?: number
  size?: number
}

interface ListPostsResult {
  items: PostListItem[]
  totalCount: number
  page: number
  size: number
}

interface CreatePostInput {
  categoryCd: string
  title: string
  content?: string
  authorId: string
  pinnedYn?: string
}

interface UpdatePostInput {
  title?: string
  content?: string
  pinnedYn?: string
}

// ============================================================
// CRUD 함수
// ============================================================

const VALID_CATEGORIES = ['NOTICE', 'DATA', 'QNA', 'MANUAL']

export async function listPosts(input: ListPostsInput): Promise<ListPostsResult> {
  const page = input.page && input.page > 0 ? input.page : 1
  const size = input.size && input.size > 0 ? Math.min(input.size, 100) : 20
  const offset = (page - 1) * size

  let whereClause = `WHERE bp.USE_YN = 'Y'`
  const params: (string | number)[] = []
  let paramIdx = 1

  if (input.categoryCd) {
    whereClause += ` AND bp.CATEGORY_CD = $${paramIdx++}`
    params.push(input.categoryCd)
  }

  if (input.search) {
    whereClause += ` AND (bp.TITLE ILIKE $${paramIdx} OR u.USER_NM ILIKE $${paramIdx})`
    params.push(`%${input.search}%`)
    paramIdx++
  }

  // 전체 건수
  const countResult = await wingPool.query(
    `SELECT COUNT(*) as cnt
     FROM BOARD_POST bp
     JOIN AUTH_USER u ON bp.AUTHOR_ID = u.USER_ID
     ${whereClause}`,
    params
  )
  const totalCount = parseInt(countResult.rows[0].cnt, 10)

  // 목록 (상단고정 우선, 등록일 내림차순)
  const listParams = [...params, size, offset]
  const listResult = await wingPool.query(
    `SELECT bp.POST_SN as sn, bp.CATEGORY_CD as category_cd, bp.TITLE as title,
            bp.AUTHOR_ID as author_id, u.USER_NM as author_name,
            bp.VIEW_CNT as view_cnt, bp.PINNED_YN as pinned_yn,
            bp.REG_DTM as reg_dtm
     FROM BOARD_POST bp
     JOIN AUTH_USER u ON bp.AUTHOR_ID = u.USER_ID
     ${whereClause}
     ORDER BY bp.PINNED_YN DESC, bp.REG_DTM DESC
     LIMIT $${paramIdx++} OFFSET $${paramIdx}`,
    listParams
  )

  const items: PostListItem[] = listResult.rows.map((r: Record<string, unknown>) => ({
    sn: r.sn as number,
    categoryCd: r.category_cd as string,
    title: r.title as string,
    authorId: r.author_id as string,
    authorName: r.author_name as string,
    viewCnt: r.view_cnt as number,
    pinnedYn: r.pinned_yn as string,
    regDtm: r.reg_dtm as string,
  }))

  return { items, totalCount, page, size }
}

export async function getPost(postSn: number): Promise<PostDetail> {
  // 조회수 증가 + 상세 조회 (단일 쿼리)
  const result = await wingPool.query(
    `UPDATE BOARD_POST SET VIEW_CNT = VIEW_CNT + 1
     WHERE POST_SN = $1 AND USE_YN = 'Y'
     RETURNING POST_SN as sn, CATEGORY_CD as category_cd, TITLE as title,
               CONTENT as content, AUTHOR_ID as author_id,
               VIEW_CNT as view_cnt, PINNED_YN as pinned_yn,
               REG_DTM as reg_dtm, MDFCN_DTM as mdfcn_dtm`,
    [postSn]
  )

  if (result.rows.length === 0) {
    throw new AuthError('게시글을 찾을 수 없습니다.', 404)
  }

  const row = result.rows[0]

  // 작성자명 조회
  const authorResult = await wingPool.query(
    'SELECT USER_NM as name FROM AUTH_USER WHERE USER_ID = $1',
    [row.author_id]
  )

  return {
    sn: row.sn,
    categoryCd: row.category_cd,
    title: row.title,
    content: row.content,
    authorId: row.author_id,
    authorName: authorResult.rows[0]?.name || '알 수 없음',
    viewCnt: row.view_cnt,
    pinnedYn: row.pinned_yn,
    regDtm: row.reg_dtm,
    mdfcnDtm: row.mdfcn_dtm,
  }
}

export async function createPost(input: CreatePostInput): Promise<{ sn: number }> {
  if (!VALID_CATEGORIES.includes(input.categoryCd)) {
    throw new AuthError('유효하지 않은 카테고리입니다.', 400)
  }

  if (!input.title || input.title.trim().length === 0) {
    throw new AuthError('제목은 필수입니다.', 400)
  }

  const result = await wingPool.query(
    `INSERT INTO BOARD_POST (CATEGORY_CD, TITLE, CONTENT, AUTHOR_ID, PINNED_YN)
     VALUES ($1, $2, $3, $4, $5)
     RETURNING POST_SN as sn`,
    [input.categoryCd, input.title.trim(), input.content || null, input.authorId, input.pinnedYn || 'N']
  )

  return { sn: result.rows[0].sn }
}

export async function updatePost(
  postSn: number,
  input: UpdatePostInput,
  requesterId: string
): Promise<void> {
  // 게시글 존재 + 작성자 확인
  const existing = await wingPool.query(
    `SELECT AUTHOR_ID as author_id FROM BOARD_POST WHERE POST_SN = $1 AND USE_YN = 'Y'`,
    [postSn]
  )

  if (existing.rows.length === 0) {
    throw new AuthError('게시글을 찾을 수 없습니다.', 404)
  }

  if (existing.rows[0].author_id !== requesterId) {
    throw new AuthError('본인의 게시글만 수정할 수 있습니다.', 403)
  }

  const sets: string[] = []
  const params: (string | number | null)[] = []
  let idx = 1

  if (input.title !== undefined) {
    sets.push(`TITLE = $${idx++}`)
    params.push(input.title.trim())
  }
  if (input.content !== undefined) {
    sets.push(`CONTENT = $${idx++}`)
    params.push(input.content)
  }
  if (input.pinnedYn !== undefined) {
    sets.push(`PINNED_YN = $${idx++}`)
    params.push(input.pinnedYn)
  }

  if (sets.length === 0) {
    throw new AuthError('수정할 항목이 없습니다.', 400)
  }

  sets.push('MDFCN_DTM = NOW()')
  params.push(postSn)

  await wingPool.query(
    `UPDATE BOARD_POST SET ${sets.join(', ')} WHERE POST_SN = $${idx}`,
    params
  )
}

// ============================================================
// 매뉴얼 CRUD
// ============================================================

interface ManualItem {
  manualSn: number
  catgNm: string
  title: string
  version: string | null
  fileTp: string | null
  fileSz: string | null
  filePath: string | null
  authorNm: string | null
  dwnldCnt: number
  regDtm: string
}

interface ListManualsInput {
  category?: string
  search?: string
}

interface CreateManualInput {
  catgNm: string
  title: string
  version?: string
  fileTp?: string
  fileSz?: string
  filePath?: string
  authorNm?: string
}

interface UpdateManualInput {
  catgNm?: string
  title?: string
  version?: string
  fileTp?: string
  fileSz?: string
  filePath?: string
}

function rowToManual(r: Record<string, unknown>): ManualItem {
  return {
    manualSn: r.manual_sn as number,
    catgNm: r.catg_nm as string,
    title: r.title as string,
    version: r.version as string | null,
    fileTp: r.file_tp as string | null,
    fileSz: r.file_sz as string | null,
    filePath: r.file_path as string | null,
    authorNm: r.author_nm as string | null,
    dwnldCnt: r.dwnld_cnt as number,
    regDtm: r.reg_dtm as string,
  }
}

export async function listManuals(input: ListManualsInput): Promise<ManualItem[]> {
  const conditions: string[] = ["USE_YN = 'Y'"]
  const params: string[] = []
  let idx = 1

  if (input.category) {
    conditions.push(`CATG_NM = $${idx++}`)
    params.push(input.category)
  }
  if (input.search) {
    conditions.push(`(TITLE ILIKE $${idx} OR AUTHOR_NM ILIKE $${idx})`)
    params.push(`%${input.search}%`)
    idx++
  }

  const { rows } = await wingPool.query(
    `SELECT MANUAL_SN, CATG_NM, TITLE, VERSION, FILE_TP, FILE_SZ,
            FILE_PATH, AUTHOR_NM, DWNLD_CNT, REG_DTM
     FROM MANUAL_FILE
     WHERE ${conditions.join(' AND ')}
     ORDER BY REG_DTM DESC`,
    params
  )

  return rows.map((r: Record<string, unknown>) => rowToManual(r))
}

export async function createManual(input: CreateManualInput): Promise<{ manualSn: number }> {
  if (!input.title || input.title.trim().length === 0) {
    throw new AuthError('제목은 필수입니다.', 400)
  }

  const { rows } = await wingPool.query(
    `INSERT INTO MANUAL_FILE (CATG_NM, TITLE, VERSION, FILE_TP, FILE_SZ, FILE_PATH, AUTHOR_NM)
     VALUES ($1, $2, $3, $4, $5, $6, $7)
     RETURNING MANUAL_SN`,
    [input.catgNm, input.title.trim(), input.version || null, input.fileTp || null, input.fileSz || null, input.filePath || null, input.authorNm || null]
  )

  return { manualSn: rows[0].manual_sn }
}

export async function updateManual(manualSn: number, input: UpdateManualInput): Promise<void> {
  const existing = await wingPool.query(
    `SELECT MANUAL_SN FROM MANUAL_FILE WHERE MANUAL_SN = $1 AND USE_YN = 'Y'`,
    [manualSn]
  )
  if (existing.rows.length === 0) {
    throw new AuthError('매뉴얼을 찾을 수 없습니다.', 404)
  }

  const sets: string[] = []
  const params: (string | number | null)[] = []
  let idx = 1

  if (input.catgNm !== undefined) { sets.push(`CATG_NM = $${idx++}`); params.push(input.catgNm) }
  if (input.title !== undefined) { sets.push(`TITLE = $${idx++}`); params.push(input.title.trim()) }
  if (input.version !== undefined) { sets.push(`VERSION = $${idx++}`); params.push(input.version) }
  if (input.fileTp !== undefined) { sets.push(`FILE_TP = $${idx++}`); params.push(input.fileTp) }
  if (input.fileSz !== undefined) { sets.push(`FILE_SZ = $${idx++}`); params.push(input.fileSz) }
  if (input.filePath !== undefined) { sets.push(`FILE_PATH = $${idx++}`); params.push(input.filePath) }

  if (sets.length === 0) {
    throw new AuthError('수정할 항목이 없습니다.', 400)
  }

  sets.push('MDFCN_DTM = NOW()')
  params.push(manualSn)

  await wingPool.query(
    `UPDATE MANUAL_FILE SET ${sets.join(', ')} WHERE MANUAL_SN = $${idx}`,
    params
  )
}

export async function deleteManual(manualSn: number): Promise<void> {
  const existing = await wingPool.query(
    `SELECT MANUAL_SN FROM MANUAL_FILE WHERE MANUAL_SN = $1 AND USE_YN = 'Y'`,
    [manualSn]
  )
  if (existing.rows.length === 0) {
    throw new AuthError('매뉴얼을 찾을 수 없습니다.', 404)
  }

  await wingPool.query(
    `UPDATE MANUAL_FILE SET USE_YN = 'N', MDFCN_DTM = NOW() WHERE MANUAL_SN = $1`,
    [manualSn]
  )
}

export async function incrementManualDownload(manualSn: number): Promise<void> {
  await wingPool.query(
    `UPDATE MANUAL_FILE SET DWNLD_CNT = DWNLD_CNT + 1 WHERE MANUAL_SN = $1 AND USE_YN = 'Y'`,
    [manualSn]
  )
}

// ============================================================
// 게시글 삭제
// ============================================================

export async function deletePost(postSn: number, requesterId: string): Promise<void> {
  // 게시글 존재 + 작성자 확인
  const existing = await wingPool.query(
    `SELECT AUTHOR_ID as author_id FROM BOARD_POST WHERE POST_SN = $1 AND USE_YN = 'Y'`,
    [postSn]
  )

  if (existing.rows.length === 0) {
    throw new AuthError('게시글을 찾을 수 없습니다.', 404)
  }

  if (existing.rows[0].author_id !== requesterId) {
    throw new AuthError('본인의 게시글만 삭제할 수 있습니다.', 403)
  }

  // 논리 삭제
  await wingPool.query(
    `UPDATE BOARD_POST SET USE_YN = 'N', MDFCN_DTM = NOW() WHERE POST_SN = $1`,
    [postSn]
  )
}

/** 관리자 전용 삭제 — 소유자 검증 없이 논리 삭제 */
export async function adminDeletePost(postSn: number): Promise<void> {
  const existing = await wingPool.query(
    `SELECT POST_SN FROM BOARD_POST WHERE POST_SN = $1 AND USE_YN = 'Y'`,
    [postSn]
  )
  if (existing.rows.length === 0) {
    throw new AuthError('게시글을 찾을 수 없습니다.', 404)
  }
  await wingPool.query(
    `UPDATE BOARD_POST SET USE_YN = 'N', MDFCN_DTM = NOW() WHERE POST_SN = $1`,
    [postSn]
  )
}
