import pg from 'pg'
import { tenantContextStorage } from './tenant-db.js'

const { Pool } = pg

const poolConfig: any = {
  host: process.env.DB_HOST || 'localhost',
  port: parseInt(process.env.DB_PORT || '5432'),
  database: process.env.DB_NAME || 'bill_query',
  user: process.env.DB_USER || 'freedak',
  max: parseInt(process.env.DB_POOL_MAX || '10'),
}
if (process.env.DB_PASSWORD) {
  poolConfig.password = process.env.DB_PASSWORD
}
const pool = new Pool(poolConfig)

pool.on('error', (err) => {
  console.error('Unexpected error on idle client', err)
})

export interface QueryResult<T = any> {
  rows: T[]
  rowCount: number | null
}

export async function query<T = any>(text: string, params?: any[], opts?: { skipScope?: boolean }): Promise<QueryResult<T>> {
  const ctx = tenantContextStorage.getStore()
  const usePool = ctx ? ctx.pool : pool
  const start = Date.now()

  let sql = text
  let sqlParams = params || []

  if (!opts?.skipScope && ctx?.scope && (ctx.scope.role === 'store' || ctx.scope.role === 'regional')) {
    const sqlLower = sql.toLowerCase()
    const scopeCodes = ctx.scope.role === 'store'
      ? ctx.scope.storeCode!.split(',').map(s => s.trim()).filter(Boolean)
      : ctx.scope.region!.split(',').map(s => s.trim()).filter(Boolean)

    if (scopeCodes.length > 0) {
      // Handle raw import tables with non-standard store columns
      const hasBillRecords = /\bbill_records\b/.test(sqlLower)
      const hasSalaryRecords = /\bsalary_detail_records\b/.test(sqlLower)
      const hasAttendanceRecords = /\battendance_records\b/.test(sqlLower)
      const rawTableCount = [hasBillRecords, hasSalaryRecords, hasAttendanceRecords].filter(Boolean).length

      let scopeClause: string | null = null
      if (rawTableCount === 1 && hasBillRecords) {
        scopeClause = ` c003 IN (SELECT store_name FROM analytics.dim_store WHERE store_code = ANY($${sqlParams.length + 1}))`
      } else if (rawTableCount === 1 && hasSalaryRecords) {
        scopeClause = ` org_level5 IN (SELECT COALESCE(m.salary_name, d.store_name) FROM analytics.dim_store d LEFT JOIN store_name_mapping m ON m.bill_name = d.store_name WHERE d.store_code = ANY($${sqlParams.length + 1}))`
      } else if (rawTableCount === 1 && hasAttendanceRecords) {
        scopeClause = ` EXISTS (SELECT 1 FROM analytics.dim_store d WHERE d.store_code = ANY($${sqlParams.length + 1}) AND ${/\bar\b/.test(sqlLower) ? 'ar' : 'attendance_records'}.department LIKE '%' || d.store_name || '%')`
      } else if (rawTableCount >= 2 && hasSalaryRecords) {
        // Multiple raw tables with salary_detail_records as main table (e.g. attendance-alert)
        scopeClause = ` s.org_level5 IN (SELECT COALESCE(m.salary_name, d.store_name) FROM analytics.dim_store d LEFT JOIN store_name_mapping m ON m.bill_name = d.store_name WHERE d.store_code = ANY($${sqlParams.length + 1}))`
      } else {
        // For normal views/tables with store_code or store_name
        const hasStoreCode = /\bstore_code\b/.test(sqlLower)
        const hasStoreName = /\bstore_name\b/.test(sqlLower)
        const hasStoreRef = hasStoreCode || hasStoreName || /v_store_|mv_store_|bill_fact|fact_bill|store_task|dim_store/.test(sqlLower)
        if (hasStoreRef) {
          // Detect main table alias to avoid ambiguous column reference
          // Look for "FROM <table> <alias>" pattern where alias is a short identifier
          const fromMatch = sql.match(/\bFROM\s+[\w.]+\s+(?:AS\s+)?(\w+)/i)
          const mainAlias = fromMatch && !['where', 'group', 'order', 'left', 'right', 'inner', 'join', 'on', 'and', 'or', 'select', 'from', 'having', 'limit', 'union'].includes(fromMatch[1].toLowerCase()) ? fromMatch[1] : null
          const colPrefix = mainAlias ? `${mainAlias}.` : ''
          scopeClause = hasStoreCode
            ? ` ${colPrefix}store_code = ANY($${sqlParams.length + 1})`
            : ` ${colPrefix}store_name IN (SELECT store_name FROM analytics.dim_store WHERE store_code = ANY($${sqlParams.length + 1}))`
        }
      }

      if (scopeClause) {
        sqlParams = [...sqlParams, scopeCodes]
        // Inject scope filter into the main WHERE clause, skipping WHERE inside FILTER(WHERE ...) and subqueries
        // Strategy: mask FILTER(WHERE), then replace the LAST WHERE (main query's WHERE comes after subquery WHEREs)
        const maskToken = '__FWHM__'
        const maskedSql = sql.replace(/FILTER\s*\(\s*WHERE\b/g, `FILTER (${maskToken}`)
        const whereMatches = [...maskedSql.matchAll(/\bWHERE\b/g)]
        if (whereMatches.length > 0) {
          // Replace the last WHERE occurrence (main query WHERE is typically last)
          const lastWhere = whereMatches[whereMatches.length - 1]
          sql = maskedSql.substring(0, lastWhere.index!) + `WHERE${scopeClause} AND` + maskedSql.substring(lastWhere.index! + 5)
          sql = sql.replace(new RegExp(maskToken, 'g'), 'WHERE')
        } else {
          // No WHERE clause: inject after the last FROM <table> in the main (non-CTE) query
          const fromMatches = [...maskedSql.matchAll(/\bFROM\s+(\w+)/g)]
          if (fromMatches.length > 0) {
            const lastFrom = fromMatches[fromMatches.length - 1]
            const insertPos = lastFrom.index! + lastFrom[0].length
            sql = maskedSql.substring(0, insertPos) + ` WHERE${scopeClause}` + maskedSql.substring(insertPos)
          } else {
            sql = sql + ` WHERE${scopeClause}`
          }
          sql = sql.replace(new RegExp(maskToken, 'g'), 'WHERE')
        }
      }
    }
  }

  const res = await usePool.query(sql, sqlParams)
  const duration = Date.now() - start
  if (duration > 500) {
    console.warn(`Slow query (${duration}ms):`, sql.substring(0, 100))
  }
  return res
}

export async function withTransaction<T>(callback: (client: pg.PoolClient) => Promise<T>): Promise<T> {
  const ctx = tenantContextStorage.getStore()
  const usePool = ctx ? ctx.pool : pool
  const client = await usePool.connect()
  try {
    await client.query('BEGIN')
    const result = await callback(client)
    await client.query('COMMIT')
    return result
  } catch (err) {
    await client.query('ROLLBACK')
    throw err
  } finally {
    client.release()
  }
}

export default pool
