'use strict'
/**
 * flow-templates.js — Hub REST API for Flow Template doctype.
 *
 * Endpoints (mounted under /flow/templates):
 *   GET    /flow/templates                   List (?category=&applies_to=&is_active=&trigger_event=)
 *   GET    /flow/templates/:name             Fetch one (flow_definition parsed)
 *   POST   /flow/templates                   Create new template
 *   PUT    /flow/templates/:name             Update + bump version
 *   DELETE /flow/templates/:name             Delete (blocked if is_system=1)
 *   POST   /flow/templates/:name/duplicate   Clone a template (new name)
 *
 * Flow definition JSON schema: see erpnext/seed_flow_templates.py header.
 */

const { log, json, parseBody, erpFetch } = require('./helpers')

const DOCTYPE = 'Flow Template'
const ENC_DOC = encodeURIComponent(DOCTYPE)

// Fields returned in list view (keep small — flow_definition is heavy)
const LIST_FIELDS = [
  'name', 'template_name', 'category', 'applies_to',
  'icon', 'description', 'is_active', 'is_system',
  'version', 'step_count', 'trigger_event', 'trigger_condition',
  'tags', 'modified',
]

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function parseFlowDefinition (raw) {
  if (!raw) return { version: 1, trigger: { event: 'manual', condition: '' }, variables: {}, steps: [] }
  if (typeof raw === 'object') return raw
  try { return JSON.parse(raw) }
  catch (e) { log('Flow Template: invalid flow_definition JSON:', e.message); return null }
}

function validateFlowDefinition (def) {
  if (!def || typeof def !== 'object') return 'flow_definition must be an object'
  if (!Array.isArray(def.steps)) return 'flow_definition.steps must be an array'

  const ids = new Set()
  for (const step of def.steps) {
    if (!step.id || typeof step.id !== 'string') return `step without id`
    if (ids.has(step.id)) return `duplicate step id: ${step.id}`
    ids.add(step.id)
    if (!step.kind) return `step ${step.id}: kind required`
    const validKinds = ['dispatch_job', 'issue', 'notify', 'webhook', 'erp_update',
                        'wait', 'condition', 'subscription_activate']
    if (!validKinds.includes(step.kind)) return `step ${step.id}: invalid kind "${step.kind}"`
    if (step.depends_on && !Array.isArray(step.depends_on)) return `step ${step.id}: depends_on must be array`
  }

  // Referential integrity: depends_on / parent_id must point to existing step IDs
  for (const step of def.steps) {
    for (const dep of step.depends_on || []) {
      if (!ids.has(dep)) return `step ${step.id}: depends_on references unknown step "${dep}"`
    }
    if (step.parent_id && !ids.has(step.parent_id)) {
      return `step ${step.id}: parent_id references unknown step "${step.parent_id}"`
    }
  }

  return null  // OK
}

function serializeFlowDefinition (def) {
  return JSON.stringify(def, null, 2)
}

function hydrate (row) {
  if (!row) return null
  const def = parseFlowDefinition(row.flow_definition)
  return { ...row, flow_definition: def }
}

// ---------------------------------------------------------------------------
// Endpoint handlers
// ---------------------------------------------------------------------------

async function listTemplates (req, res, params) {
  const filters = []
  if (params.category)      filters.push(['category', '=', params.category])
  if (params.applies_to)    filters.push(['applies_to', '=', params.applies_to])
  if (params.trigger_event) filters.push(['trigger_event', '=', params.trigger_event])
  if (params.is_active !== undefined) {
    filters.push(['is_active', '=', params.is_active === '1' || params.is_active === 'true' ? 1 : 0])
  }
  if (params.q) filters.push(['template_name', 'like', `%${params.q}%`])

  const qs = new URLSearchParams({
    fields: JSON.stringify(LIST_FIELDS),
    filters: JSON.stringify(filters),
    limit_page_length: String(params.limit || 200),
    order_by: 'template_name asc',
  })
  const r = await erpFetch(`/api/resource/${ENC_DOC}?${qs.toString()}`)
  if (r.status !== 200) return json(res, r.status, { error: 'Failed to list', detail: r.data })
  return json(res, 200, { templates: r.data.data || [] })
}

async function getTemplate (req, res, name) {
  const r = await erpFetch(`/api/resource/${ENC_DOC}/${encodeURIComponent(name)}`)
  if (r.status !== 200) return json(res, r.status, { error: 'Not found' })
  return json(res, 200, { template: hydrate(r.data.data) })
}

async function createTemplate (req, res) {
  const body = await parseBody(req)
  if (!body.template_name) return json(res, 400, { error: 'template_name required' })
  if (!body.flow_definition) return json(res, 400, { error: 'flow_definition required' })

  const def = typeof body.flow_definition === 'string'
    ? parseFlowDefinition(body.flow_definition)
    : body.flow_definition
  const err = validateFlowDefinition(def)
  if (err) return json(res, 400, { error: 'Invalid flow_definition', detail: err })

  const payload = {
    doctype: DOCTYPE,
    template_name: body.template_name,
    category: body.category || 'Custom',
    applies_to: body.applies_to || 'Service Contract',
    icon: body.icon || 'account_tree',
    description: body.description || '',
    trigger_event: body.trigger_event || 'manual',
    trigger_condition: body.trigger_condition || '',
    is_active: body.is_active === false ? 0 : 1,
    is_system: 0,  // API cannot create system templates
    version: 1,
    flow_definition: serializeFlowDefinition(def),
    step_count: def.steps.length,
    tags: body.tags || '',
    notes: body.notes || '',
  }

  const r = await erpFetch(`/api/resource/${ENC_DOC}`, {
    method: 'POST', body: JSON.stringify(payload),
  })
  if (r.status !== 200 && r.status !== 201) {
    return json(res, r.status, { error: 'Create failed', detail: r.data })
  }
  log(`Flow Template created: ${r.data.data?.name} (${body.template_name})`)
  return json(res, 201, { template: hydrate(r.data.data) })
}

async function updateTemplate (req, res, name) {
  const body = await parseBody(req)

  // Fetch current to check is_system + increment version
  const cur = await erpFetch(`/api/resource/${ENC_DOC}/${encodeURIComponent(name)}`)
  if (cur.status !== 200) return json(res, 404, { error: 'Not found' })
  const current = cur.data.data

  const patch = {}
  // Always-editable fields
  for (const f of ['template_name', 'category', 'applies_to', 'icon', 'description',
                   'trigger_event', 'trigger_condition', 'is_active', 'tags', 'notes']) {
    if (body[f] !== undefined) patch[f] = body[f]
  }

  // Flow definition — validate
  if (body.flow_definition !== undefined) {
    const def = typeof body.flow_definition === 'string'
      ? parseFlowDefinition(body.flow_definition)
      : body.flow_definition
    const err = validateFlowDefinition(def)
    if (err) return json(res, 400, { error: 'Invalid flow_definition', detail: err })
    patch.flow_definition = serializeFlowDefinition(def)
    patch.step_count = def.steps.length
  }

  // Bump version on any change
  if (Object.keys(patch).length > 0) {
    patch.version = (current.version || 0) + 1
  }

  const r = await erpFetch(`/api/resource/${ENC_DOC}/${encodeURIComponent(name)}`, {
    method: 'PUT', body: JSON.stringify(patch),
  })
  if (r.status !== 200) return json(res, r.status, { error: 'Update failed', detail: r.data })
  log(`Flow Template updated: ${name} -> v${patch.version}`)
  return json(res, 200, { template: hydrate(r.data.data) })
}

async function deleteTemplate (req, res, name) {
  // Check is_system
  const cur = await erpFetch(`/api/resource/${ENC_DOC}/${encodeURIComponent(name)}`)
  if (cur.status !== 200) return json(res, 404, { error: 'Not found' })
  if (cur.data.data.is_system) {
    return json(res, 403, {
      error: 'System template cannot be deleted',
      hint: 'Use is_active=0 to disable, or duplicate then modify the copy.',
    })
  }

  const r = await erpFetch(`/api/resource/${ENC_DOC}/${encodeURIComponent(name)}`, { method: 'DELETE' })
  if (r.status !== 200 && r.status !== 202) {
    return json(res, r.status, { error: 'Delete failed', detail: r.data })
  }
  log(`Flow Template deleted: ${name}`)
  return json(res, 200, { ok: true })
}

async function duplicateTemplate (req, res, name) {
  const body = await parseBody(req).catch(() => ({}))
  const cur = await erpFetch(`/api/resource/${ENC_DOC}/${encodeURIComponent(name)}`)
  if (cur.status !== 200) return json(res, 404, { error: 'Not found' })
  const src = cur.data.data
  const def = parseFlowDefinition(src.flow_definition)

  const newName = body.template_name || `${src.template_name} (copie)`
  const payload = {
    doctype: DOCTYPE,
    template_name: newName,
    category: src.category,
    applies_to: src.applies_to,
    icon: src.icon,
    description: src.description,
    trigger_event: src.trigger_event,
    trigger_condition: src.trigger_condition,
    is_active: 0,  // cloned = inactive until user reviews
    is_system: 0,
    version: 1,
    flow_definition: serializeFlowDefinition(def),
    step_count: def.steps?.length || 0,
    tags: src.tags || '',
    notes: `(Copie de ${src.template_name})${src.notes ? '\n' + src.notes : ''}`,
  }

  const r = await erpFetch(`/api/resource/${ENC_DOC}`, {
    method: 'POST', body: JSON.stringify(payload),
  })
  if (r.status !== 200 && r.status !== 201) {
    return json(res, r.status, { error: 'Duplicate failed', detail: r.data })
  }
  log(`Flow Template duplicated: ${name} -> ${r.data.data?.name}`)
  return json(res, 201, { template: hydrate(r.data.data) })
}

// ---------------------------------------------------------------------------
// Router
// ---------------------------------------------------------------------------

async function handle (req, res, method, urlPath, urlObj) {
  // /flow/templates/:name/duplicate
  const duplicateMatch = urlPath.match(/^\/flow\/templates\/([^/]+)\/duplicate$/)
  if (duplicateMatch && method === 'POST') {
    return duplicateTemplate(req, res, decodeURIComponent(duplicateMatch[1]))
  }

  // /flow/templates/:name
  const oneMatch = urlPath.match(/^\/flow\/templates\/([^/]+)$/)
  if (oneMatch) {
    const name = decodeURIComponent(oneMatch[1])
    if (method === 'GET')    return getTemplate(req, res, name)
    if (method === 'PUT')    return updateTemplate(req, res, name)
    if (method === 'DELETE') return deleteTemplate(req, res, name)
  }

  // /flow/templates
  if (urlPath === '/flow/templates') {
    if (method === 'GET') {
      const params = {}
      if (urlObj && urlObj.searchParams) {
        for (const [k, v] of urlObj.searchParams) params[k] = v
      }
      return listTemplates(req, res, params)
    }
    if (method === 'POST') return createTemplate(req, res)
  }

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

module.exports = { handle, parseFlowDefinition, validateFlowDefinition }
