import { authPool } from '../db/authDb.js'
import { hashPassword, AuthError } from '../auth/authService.js'
import { getSettingBoolean } from '../settings/settingsService.js'

interface UserListItem {
  id: string
  account: string
  name: string
  rank: string | null
  orgSn: number | null
  orgName: string | null
  orgAbbr: string | null
  status: string
  failCount: number
  lastLogin: string | null
  roles: string[]
  roleSns: number[]
  regDtm: string
  oauthProvider: string | null
  email: string | null
}

interface CreateUserInput {
  account: string
  password: string
  name: string
  rank?: string
  orgSn?: number
  roleSns?: number[]
}

interface UpdateUserInput {
  name?: string
  rank?: string
  orgSn?: number | null
  status?: string
}

export async function listUsers(search?: string, status?: string): Promise<UserListItem[]> {
  let query = `
    SELECT u.USER_ID as id, u.USER_ACNT as account, u.USER_NM as name,
           u.RNKP_NM as rank, u.ORG_SN as org_sn,
           o.ORG_NM as org_name, o.ORG_ABBR_NM as org_abbr,
           u.USER_STTS_CD as status, u.FAIL_CNT as fail_count,
           u.LAST_LOGIN_DTM as last_login, u.REG_DTM as reg_dtm,
           u.OAUTH_PROVIDER as oauth_provider, u.EMAIL as email
    FROM AUTH_USER u
    LEFT JOIN AUTH_ORG o ON u.ORG_SN = o.ORG_SN
    WHERE 1=1
  `
  const params: (string | undefined)[] = []
  let paramIdx = 1

  if (search) {
    query += ` AND (u.USER_ACNT ILIKE $${paramIdx} OR u.USER_NM ILIKE $${paramIdx})`
    params.push(`%${search}%`)
    paramIdx++
  }

  if (status) {
    query += ` AND u.USER_STTS_CD = $${paramIdx}`
    params.push(status)
    paramIdx++
  }

  query += ' ORDER BY u.REG_DTM DESC'

  const result = await authPool.query(query, params)

  const users: UserListItem[] = []
  for (const row of result.rows) {
    const rolesResult = await authPool.query(
      `SELECT r.ROLE_CD, r.ROLE_SN FROM AUTH_USER_ROLE ur JOIN AUTH_ROLE r ON ur.ROLE_SN = r.ROLE_SN WHERE ur.USER_ID = $1`,
      [row.id]
    )
    users.push({
      id: row.id,
      account: row.account,
      name: row.name,
      rank: row.rank,
      orgSn: row.org_sn,
      orgName: row.org_name,
      orgAbbr: row.org_abbr,
      status: row.status,
      failCount: row.fail_count,
      lastLogin: row.last_login,
      roles: rolesResult.rows.map((r: { role_cd: string }) => r.role_cd),
      roleSns: rolesResult.rows.map((r: { role_sn: number }) => r.role_sn),
      regDtm: row.reg_dtm,
      oauthProvider: row.oauth_provider,
      email: row.email,
    })
  }

  return users
}

export async function getUser(userId: string): Promise<UserListItem> {
  const result = await authPool.query(
    `SELECT u.USER_ID as id, u.USER_ACNT as account, u.USER_NM as name,
            u.RNKP_NM as rank, u.ORG_SN as org_sn,
            o.ORG_NM as org_name, o.ORG_ABBR_NM as org_abbr,
            u.USER_STTS_CD as status, u.FAIL_CNT as fail_count,
            u.LAST_LOGIN_DTM as last_login, u.REG_DTM as reg_dtm,
            u.OAUTH_PROVIDER as oauth_provider, u.EMAIL as email
     FROM AUTH_USER u
     LEFT JOIN AUTH_ORG o ON u.ORG_SN = o.ORG_SN
     WHERE u.USER_ID = $1`,
    [userId]
  )

  if (result.rows.length === 0) {
    throw new AuthError('사용자를 찾을 수 없습니다.', 404)
  }

  const row = result.rows[0]

  const rolesResult = await authPool.query(
    `SELECT r.ROLE_CD, r.ROLE_SN FROM AUTH_USER_ROLE ur JOIN AUTH_ROLE r ON ur.ROLE_SN = r.ROLE_SN WHERE ur.USER_ID = $1`,
    [userId]
  )

  return {
    id: row.id,
    account: row.account,
    name: row.name,
    rank: row.rank,
    orgSn: row.org_sn,
    orgName: row.org_name,
    orgAbbr: row.org_abbr,
    status: row.status,
    failCount: row.fail_count,
    lastLogin: row.last_login,
    roles: rolesResult.rows.map((r: { role_cd: string }) => r.role_cd),
    roleSns: rolesResult.rows.map((r: { role_sn: number }) => r.role_sn),
    regDtm: row.reg_dtm,
    oauthProvider: row.oauth_provider,
    email: row.email,
  }
}

export async function createUser(input: CreateUserInput): Promise<{ id: string }> {
  const existing = await authPool.query(
    'SELECT 1 FROM AUTH_USER WHERE USER_ACNT = $1',
    [input.account]
  )
  if (existing.rows.length > 0) {
    throw new AuthError('이미 존재하는 계정입니다.', 409)
  }

  const pswdHash = await hashPassword(input.password)

  // 자동 승인 설정 확인
  const autoApprove = await getSettingBoolean('registration.auto-approve', true)
  const initialStatus = autoApprove ? 'ACTIVE' : 'PENDING'

  const result = await authPool.query(
    `INSERT INTO AUTH_USER (USER_ACNT, PSWD_HASH, USER_NM, RNKP_NM, ORG_SN, USER_STTS_CD)
     VALUES ($1, $2, $3, $4, $5, $6)
     RETURNING USER_ID as id`,
    [input.account, pswdHash, input.name, input.rank || null, input.orgSn || null, initialStatus]
  )

  const userId = result.rows[0].id

  // 역할 할당
  if (input.roleSns && input.roleSns.length > 0) {
    // 명시적으로 역할 지정된 경우
    for (const roleSn of input.roleSns) {
      await authPool.query(
        'INSERT INTO AUTH_USER_ROLE (USER_ID, ROLE_SN) VALUES ($1, $2)',
        [userId, roleSn]
      )
    }
  } else {
    // 기본 역할 자동 할당 설정 확인
    const useDefaultRole = await getSettingBoolean('registration.default-role', true)
    if (useDefaultRole) {
      const defaultRoles = await authPool.query(
        "SELECT ROLE_SN FROM AUTH_ROLE WHERE DFLT_YN = 'Y'"
      )
      for (const row of defaultRoles.rows) {
        await authPool.query(
          'INSERT INTO AUTH_USER_ROLE (USER_ID, ROLE_SN) VALUES ($1, $2)',
          [userId, row.role_sn]
        )
      }
    }
  }

  return { id: userId }
}

export async function approveUser(userId: string): Promise<void> {
  const result = await authPool.query(
    'SELECT USER_STTS_CD FROM AUTH_USER WHERE USER_ID = $1',
    [userId]
  )
  if (result.rows.length === 0) {
    throw new AuthError('사용자를 찾을 수 없습니다.', 404)
  }
  if (result.rows[0].user_stts_cd !== 'PENDING') {
    throw new AuthError('승인 대기 상태의 사용자만 승인할 수 있습니다.', 400)
  }

  await authPool.query(
    "UPDATE AUTH_USER SET USER_STTS_CD = 'ACTIVE', MDFCN_DTM = NOW() WHERE USER_ID = $1",
    [userId]
  )

  // 기본 역할이 아직 할당되지 않았으면 할당
  const existingRoles = await authPool.query(
    'SELECT 1 FROM AUTH_USER_ROLE WHERE USER_ID = $1',
    [userId]
  )
  if (existingRoles.rows.length === 0) {
    const useDefaultRole = await getSettingBoolean('registration.default-role', true)
    if (useDefaultRole) {
      const defaultRoles = await authPool.query(
        "SELECT ROLE_SN FROM AUTH_ROLE WHERE DFLT_YN = 'Y'"
      )
      for (const row of defaultRoles.rows) {
        await authPool.query(
          'INSERT INTO AUTH_USER_ROLE (USER_ID, ROLE_SN) VALUES ($1, $2)',
          [userId, row.role_sn]
        )
      }
    }
  }
}

export async function rejectUser(userId: string): Promise<void> {
  const result = await authPool.query(
    'SELECT USER_STTS_CD FROM AUTH_USER WHERE USER_ID = $1',
    [userId]
  )
  if (result.rows.length === 0) {
    throw new AuthError('사용자를 찾을 수 없습니다.', 404)
  }
  if (result.rows[0].user_stts_cd !== 'PENDING') {
    throw new AuthError('승인 대기 상태의 사용자만 거절할 수 있습니다.', 400)
  }

  await authPool.query(
    "UPDATE AUTH_USER SET USER_STTS_CD = 'REJECTED', MDFCN_DTM = NOW() WHERE USER_ID = $1",
    [userId]
  )
}

export async function updateUser(userId: string, input: UpdateUserInput): Promise<void> {
  const sets: string[] = []
  const params: (string | number | null)[] = []
  let idx = 1

  if (input.name !== undefined) {
    sets.push(`USER_NM = $${idx++}`)
    params.push(input.name)
  }
  if (input.rank !== undefined) {
    sets.push(`RNKP_NM = $${idx++}`)
    params.push(input.rank)
  }
  if (input.orgSn !== undefined) {
    sets.push(`ORG_SN = $${idx++}`)
    params.push(input.orgSn)
  }
  if (input.status !== undefined) {
    sets.push(`USER_STTS_CD = $${idx++}`)
    params.push(input.status)
    if (input.status === 'ACTIVE') {
      sets.push('FAIL_CNT = 0')
    }
  }

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

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

  await authPool.query(
    `UPDATE AUTH_USER SET ${sets.join(', ')} WHERE USER_ID = $${idx}`,
    params
  )
}

export async function changePassword(userId: string, newPassword: string): Promise<void> {
  const pswdHash = await hashPassword(newPassword)
  await authPool.query(
    'UPDATE AUTH_USER SET PSWD_HASH = $1, MDFCN_DTM = NOW() WHERE USER_ID = $2',
    [pswdHash, userId]
  )
}

// ── 조직 목록 조회 ──

interface OrgItem {
  orgSn: number
  orgNm: string
  orgAbbrNm: string | null
  orgTpCd: string
  upperOrgSn: number | null
}

export async function listOrgs(): Promise<OrgItem[]> {
  const { rows } = await authPool.query(
    `SELECT ORG_SN, ORG_NM, ORG_ABBR_NM, ORG_TP_CD, UPPER_ORG_SN
     FROM AUTH_ORG
     ORDER BY ORG_SN`
  )
  return rows.map((r: Record<string, unknown>) => ({
    orgSn: r.org_sn as number,
    orgNm: r.org_nm as string,
    orgAbbrNm: r.org_abbr_nm as string | null,
    orgTpCd: r.org_tp_cd as string,
    upperOrgSn: r.upper_org_sn as number | null,
  }))
}

export async function assignRoles(userId: string, roleSns: number[]): Promise<void> {
  await authPool.query('DELETE FROM AUTH_USER_ROLE WHERE USER_ID = $1', [userId])

  for (const roleSn of roleSns) {
    await authPool.query(
      'INSERT INTO AUTH_USER_ROLE (USER_ID, ROLE_SN) VALUES ($1, $2)',
      [userId, roleSn]
    )
  }
}
