import { Pool } from 'pg'
import type { PoolClient, QueryResult, QueryResultRow } from 'pg'

import { runtimeConfig } from '../config/runtime.js'
import { logWarn } from '../utils/logger.js'

let poolInstance: Pool | null = null

export function getDb(): Pool {
  if (!poolInstance) {
    const pool = new Pool({
      connectionString: String(runtimeConfig.database?.url || '').trim(),
      ssl: runtimeConfig.database?.ssl ? { rejectUnauthorized: false } : false,
      max: Number(runtimeConfig.database?.maxConnections || 10),
      idleTimeoutMillis: Number(runtimeConfig.database?.idleTimeoutMs || 30_000),
      connectionTimeoutMillis: Number(runtimeConfig.database?.connectionTimeoutMs || 5_000),
      statement_timeout: Number(runtimeConfig.database?.statementTimeoutMs || 15_000),
    })
    pool.on('error', (error) => {
      void logWarn('[db/pool]', '数据库连接池出现空闲连接错误', {
        error: error instanceof Error ? error.message : String(error),
      })
    })
    poolInstance = pool
  }

  return poolInstance
}

export async function query(text: string, params?: unknown[]): Promise<QueryResult<any>>
export async function query<T extends QueryResultRow>(
  text: string,
  params?: unknown[],
): Promise<QueryResult<T>>
export async function query<T extends QueryResultRow>(
  text: string,
  params: unknown[] = [],
): Promise<QueryResult<T>> {
  const pool = getDb()
  const startedAt = process.hrtime.bigint()
  try {
    const result = await pool.query<T>(text, params)
    logSlowQuery(text, startedAt, result.rowCount ?? 0)
    return result
  } catch (error) {
    logSlowQuery(text, startedAt, 0, error)
    throw error
  }
}

function logSlowQuery(text: string, startedAt: bigint, rowCount: number, error?: unknown) {
  const thresholdMs = Number(runtimeConfig.database?.slowQueryThresholdMs ?? 500)
  if (thresholdMs <= 0) return
  const durationMs = Number(process.hrtime.bigint() - startedAt) / 1_000_000
  if (durationMs < thresholdMs && !error) return
  void logWarn('[db/query]', error ? '数据库 SQL 执行失败' : '数据库慢查询', {
    durationMs: Math.round(durationMs * 100) / 100,
    thresholdMs,
    rowCount,
    sql: fingerprintSql(text),
    error: error instanceof Error ? error.message : undefined,
  })
}

/** 只保留可聚合的 SQL 形状，参数值始终不写入日志。 */
function fingerprintSql(text: string) {
  return String(text || '')
    .replace(/--[^\r\n]*/g, ' ')
    .replace(/\/\*[\s\S]*?\*\//g, ' ')
    .replace(/\$\d+/g, '?')
    .replace(/\s+/g, ' ')
    .trim()
    .slice(0, 500)
}

export async function withTransaction<T>(fn: (client: PoolClient) => Promise<T>): Promise<T> {
  const client = await getDb().connect()

  try {
    await client.query('BEGIN')
    const result = await fn(client)
    await client.query('COMMIT')
    return result
  } catch (error) {
    await client.query('ROLLBACK')
    throw error
  } finally {
    client.release()
  }
}

export async function closeDb(): Promise<void> {
  if (!poolInstance) {
    return
  }

  const pool = poolInstance
  poolInstance = null
  await pool.end()
}
