'use strict'
/**
 * flow-api.js — HTTP endpoints for the flow runtime.
 *
 *   POST /flow/start        { template, doctype?, docname?, customer?, variables? }
 *   POST /flow/advance      { run }                   — re-evaluate a run (idempotent)
 *   POST /flow/complete     { run, step_id, result? } — external completion (scheduler / webhook / manual)
 *   POST /flow/event        { event, ctx? }           — dispatch a trigger event to all listening templates
 *   GET  /flow/runs         ?customer=…&template=…&status=…
 *   GET  /flow/runs/:name   — fetch a run (with step_state parsed)
 *
 * Security: internal endpoints (start/advance/complete/event) optionally require
 * cfg.INTERNAL_TOKEN via `Authorization: Bearer …` so only the ERPNext scheduler
 * and trusted services can launch flows.
 */

const { log, json, parseBody, erpFetch } = require('./helpers')
const runtime = require('./flow-runtime')
const cfg = require('./config')

const ENC_FR = encodeURIComponent('Flow Run')

// ── Auth guard (soft — allows open in dev when INTERNAL_TOKEN is unset) ────

function checkInternalAuth (req) {
  if (!cfg.INTERNAL_TOKEN) return true
  const a = req.headers.authorization || ''
  return a === 'Bearer ' + cfg.INTERNAL_TOKEN
}

// ── Handlers ────────────────────────────────────────────────────────────────

async function startFlow (req, res) {
  if (!checkInternalAuth(req)) return json(res, 401, { error: 'Unauthorized' })
  const body = await parseBody(req)
  if (!body.template) return json(res, 400, { error: 'template required' })
  try {
    const result = await runtime.startFlow(body.template, {
      doctype:      body.doctype,
      docname:      body.docname,
      customer:     body.customer,
      variables:    body.variables || {},
      triggerEvent: body.trigger_event || body.triggerEvent,
    })
    return json(res, 201, result)
  } catch (e) {
    log('[flow-api] start failed:', e.message)
    return json(res, 500, { error: e.message })
  }
}

// USER-FACING (proxy-authed) : lancer un flow depuis l'UI Ops, sans checkInternalAuth (/flow/* est déjà protégé par la passerelle proxy). startFlow reste réservé au scheduler/interne.
async function instantiateFlow (req, res) {
  const body = await parseBody(req)
  if (!body.template) return json(res, 400, { error: 'template required' })
  try {
    const result = await runtime.startFlow(body.template, {
      doctype: body.doctype, docname: body.docname, customer: body.customer,
      variables: body.variables || {}, triggerEvent: body.trigger_event || body.triggerEvent || 'manual',
    })
    return json(res, 201, result)
  } catch (e) { log('[flow-api] instantiate failed:', e.message); return json(res, 500, { error: e.message }) }
}

async function advanceFlow (req, res) {
  if (!checkInternalAuth(req)) return json(res, 401, { error: 'Unauthorized' })
  const body = await parseBody(req)
  if (!body.run) return json(res, 400, { error: 'run required' })
  try { return json(res, 200, await runtime.advanceFlow(body.run)) }
  catch (e) { return json(res, 500, { error: e.message }) }
}

async function completeStep (req, res) {
  if (!checkInternalAuth(req)) return json(res, 401, { error: 'Unauthorized' })
  const body = await parseBody(req)
  if (!body.run || !body.step_id) return json(res, 400, { error: 'run and step_id required' })
  try { return json(res, 200, await runtime.completeStep(body.run, body.step_id, body.result || {})) }
  catch (e) { return json(res, 500, { error: e.message }) }
}

async function dispatchEvent (req, res) {
  if (!checkInternalAuth(req)) return json(res, 401, { error: 'Unauthorized' })
  const body = await parseBody(req)
  if (!body.event) return json(res, 400, { error: 'event required' })
  try {
    const results = await runtime.dispatchEvent(body.event, body.ctx || {})
    return json(res, 200, { event: body.event, started: results.length, results })
  } catch (e) {
    return json(res, 500, { error: e.message })
  }
}

async function listRuns (req, res, urlObj) {
  const p = Object.fromEntries(urlObj.searchParams)
  const filters = []
  if (p.customer)  filters.push(['customer', '=', p.customer])
  if (p.template)  filters.push(['flow_template', '=', p.template])
  if (p.status)    filters.push(['status', '=', p.status])
  if (p.doctype && p.docname) {
    filters.push(['context_doctype', '=', p.doctype])
    filters.push(['context_docname', '=', p.docname])
  }
  const qs = new URLSearchParams({
    fields: JSON.stringify(['name', 'flow_template', 'status', 'customer', 'context_doctype', 'context_docname', 'started_at', 'completed_at', 'last_error']),
    filters: JSON.stringify(filters),
    limit_page_length: String(p.limit || 50),
    order_by: 'started_at desc',
  })
  const r = await erpFetch(`/api/resource/${ENC_FR}?${qs}`)
  if (r.status !== 200) return json(res, r.status, { error: 'Failed to list', detail: r.data })
  return json(res, 200, { runs: r.data.data || [] })
}

async function getRun (req, res, name) {
  const r = await erpFetch(`/api/resource/${ENC_FR}/${encodeURIComponent(name)}`)
  if (r.status !== 200) return json(res, r.status, { error: 'Not found' })
  const run = r.data.data
  try { run.variables = JSON.parse(run.variables || '{}') } catch { /* noop */ }
  try { run.step_state = JSON.parse(run.step_state || '{}') } catch { /* noop */ }
  return json(res, 200, { run })
}

// ── Router ──────────────────────────────────────────────────────────────────

async function handle (req, res, method, urlPath, urlObj) {
  if (urlPath === '/flow/start'    && method === 'POST') return startFlow(req, res)
  if (urlPath === '/flow/instantiate' && method === 'POST') return instantiateFlow(req, res)
  if (urlPath === '/flow/advance'  && method === 'POST') return advanceFlow(req, res)
  if (urlPath === '/flow/complete' && method === 'POST') return completeStep(req, res)
  if (urlPath === '/flow/event'    && method === 'POST') return dispatchEvent(req, res)

  const runMatch = urlPath.match(/^\/flow\/runs\/([^/]+)$/)
  if (runMatch && method === 'GET') return getRun(req, res, decodeURIComponent(runMatch[1]))

  if (urlPath === '/flow/runs' && method === 'GET') return listRuns(req, res, urlObj)

  return json(res, 404, { error: 'Flow endpoint not found' })
}

module.exports = { handle }
