const express = require('express')
const cors = require('cors')
const dotenv = require('dotenv')
const midtransClient = require('midtrans-client')

dotenv.config()

const app = express()
app.use(cors())
app.use(express.json())

const isProduction = process.env.MIDTRANS_IS_PRODUCTION === 'true'
const serverKey = process.env.MIDTRANS_SERVER_KEY || ''
const clientKey = process.env.MIDTRANS_CLIENT_KEY || ''

if (!serverKey || !clientKey) {
  console.warn('[Midtrans] Missing server/client keys in environment variables')
}

const core = new midtransClient.CoreApi({
  isProduction,
  serverKey,
  clientKey,
})

// --- Logger utilities
const LOG_LEVEL = (process.env.LOG_LEVEL || 'info').toLowerCase()
const levelOrder = { debug: 0, info: 1, warn: 2, error: 3 }
function shouldLog(level) { return (levelOrder[level] ?? 1) >= (levelOrder[LOG_LEVEL] ?? 1) }
function ts() { return new Date().toISOString() }
function sanitize(obj) {
  try { return JSON.parse(JSON.stringify(obj)) } catch { return obj }
}
function maskPayload(obj) {
  const o = sanitize(obj)
  const maskKeys = ['card_number', 'cvv', 'token_id', 'server_key']
  function mask(o2) {
    if (!o2 || typeof o2 !== 'object') return o2
    for (const k of Object.keys(o2)) {
      const v = o2[k]
      if (maskKeys.includes(k)) o2[k] = v ? '***' : v
      else if (typeof v === 'object') o2[k] = mask(v)
    }
    return o2
  }
  return mask(o)
}
function log(level, msg, meta) {
  if (!shouldLog(level)) return
  const line = `[${ts()}] [${level}] ${msg}`
  if (meta) {
    const data = typeof meta === 'string' ? meta : JSON.stringify(meta)
    console.log(line, data)
  } else {
    console.log(line)
  }
}
const logDebug = (m, meta) => log('debug', m, meta)
const logInfo = (m, meta) => log('info', m, meta)
const logWarn = (m, meta) => log('warn', m, meta)
const logError = (m, meta) => log('error', m, meta)

// Request ID + basic request/response logging
function newReqId() {
  return Math.random().toString(36).slice(2) + Date.now().toString(36)
}
app.use((req, res, next) => {
  req.id = newReqId()
  logInfo('req.start', { id: req.id, method: req.method, url: req.originalUrl })
  res.on('finish', () => {
    logInfo('req.end', { id: req.id, statusCode: res.statusCode })
  })
  next()
})

function parseEnable(v) {
  if (typeof v === 'string') {
    const s = v.trim().toLowerCase()
    return s === 'true' || s === '1' || s === 'yes' || s === 'on'
  }
  if (typeof v === 'boolean') return v
  if (typeof v === 'number') return v === 1
  return true
}

const ENABLE = {
  bank_transfer: parseEnable(process.env.ENABLE_BANK_TRANSFER),
  credit_card: parseEnable(process.env.ENABLE_CREDIT_CARD),
  gopay: parseEnable(process.env.ENABLE_GOPAY),
  cstore: parseEnable(process.env.ENABLE_CSTORE),
}

// Health check
app.get('/api/health', (_req, res) => {
  logDebug('health.check', { isProduction, hasServerKey: !!serverKey, hasClientKey: !!clientKey })
  res.json({ ok: true, env: { isProduction, hasServerKey: !!serverKey, hasClientKey: !!clientKey } })
})

// Runtime config (feature toggles)
app.get('/api/config', (_req, res) => {
  const payload = {
    paymentToggles: { ...ENABLE },
    midtransEnv: isProduction ? 'production' : 'sandbox',
    clientKey,
  }
  logDebug('config.get', payload)
  res.json(payload)
})

// Dev-only: allow updating toggles at runtime without restart
app.post('/api/config', (req, res) => {
  const isDev = process.env.NODE_ENV !== 'production'
  if (!isDev) return res.status(403).json({ error: 'FORBIDDEN', message: 'Runtime config updates disabled in production.' })
  const t = req?.body?.paymentToggles
  if (t && typeof t === 'object') {
    if (typeof t.bank_transfer !== 'undefined') ENABLE.bank_transfer = parseEnable(t.bank_transfer)
    if (typeof t.credit_card !== 'undefined') ENABLE.credit_card = parseEnable(t.credit_card)
    if (typeof t.gopay !== 'undefined') ENABLE.gopay = parseEnable(t.gopay)
    if (typeof t.cstore !== 'undefined') ENABLE.cstore = parseEnable(t.cstore)
  }
  const result = { paymentToggles: { ...ENABLE } }
  logInfo('config.post', result)
  res.json(result)
})

// Charge endpoint (pass-through to Midtrans Core API)
app.post('/api/payments/charge', async (req, res) => {
  try {
    const pt = req?.body?.payment_type
    logInfo('charge.request', { id: req.id, payment_type: pt })
    logDebug('charge.payload', maskPayload(req.body))
    const isBankType = pt === 'bank_transfer' || pt === 'echannel' || pt === 'permata'
    if (isBankType && !ENABLE.bank_transfer) {
      logWarn('charge.blocked', { id: req.id, reason: 'bank_transfer disabled' })
      return res.status(403).json({ error: 'PAYMENT_TYPE_DISABLED', message: 'Bank transfer is disabled by environment configuration.' })
    }
    if (pt === 'credit_card' && !ENABLE.credit_card) {
      logWarn('charge.blocked', { id: req.id, reason: 'credit_card disabled' })
      return res.status(403).json({ error: 'PAYMENT_TYPE_DISABLED', message: 'Credit card is disabled by environment configuration.' })
    }
    if (pt === 'gopay' && !ENABLE.gopay) {
      logWarn('charge.blocked', { id: req.id, reason: 'gopay disabled' })
      return res.status(403).json({ error: 'PAYMENT_TYPE_DISABLED', message: 'GoPay/QRIS is disabled by environment configuration.' })
    }
    if (pt === 'cstore' && !ENABLE.cstore) {
      logWarn('charge.blocked', { id: req.id, reason: 'cstore disabled' })
      return res.status(403).json({ error: 'PAYMENT_TYPE_DISABLED', message: 'Convenience Store is disabled by environment configuration.' })
    }
    const chargeResponse = await core.charge(req.body)
    logInfo('charge.success', { id: req.id, order_id: chargeResponse?.order_id, status_code: chargeResponse?.status_code })
    res.json(chargeResponse)
  } catch (e) {
    const msg = e?.message || 'Charge failed'
    logError('charge.error', { id: req.id, message: msg })
    res.status(400).json({ error: 'CHARGE_ERROR', message: msg })
  }
})

// Status endpoint (by order_id)
app.get('/api/payments/:orderId/status', async (req, res) => {
  const { orderId } = req.params
  try {
    // midtrans-client exposes transaction helper via CoreApi
    logInfo('status.request', { id: req.id, orderId })
    const status = await core.transaction.status(orderId)
    logInfo('status.success', { id: req.id, orderId, transaction_status: status?.transaction_status })
    res.json(status)
  } catch (e) {
    const msg = e?.message || 'Status check failed'
    logError('status.error', { id: req.id, orderId, message: msg })
    res.status(400).json({ error: 'STATUS_ERROR', message: msg })
  }
})

const port = process.env.PORT || 8000
app.listen(port, () => {
  console.log(`[server] listening on http://localhost:${port}/ (production=${isProduction})`)
})