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

// ── ERPNext GL query helpers ──────────────────────────────────────────────

/**
 * Query GL Entry grouped by account & month.
 * Used for revenue report and tax report.
 * ERPNext API: frappe.client.get_list with group_by isn't great for aggregation,
 * so we use the report_builder run endpoint instead.
 */

async function glQuery (filters, fields, groupBy, orderBy, limit = 500) {
  const params = new URLSearchParams({
    doctype: 'GL Entry',
    filters: JSON.stringify(filters),
    fields: JSON.stringify(fields),
    group_by: groupBy || '',
    order_by: orderBy || 'posting_date asc',
    limit_page_length: limit,
  })
  const res = await erpFetch('/api/resource/GL Entry?' + params)
  if (res.status !== 200) {
    log('GL query error:', res.status, res.statusText)
    return []
  }
  return res.data?.data || []
}

/**
 * Fetch raw GL entries for a date range + accounts.
 * Returns [{posting_date, account, debit, credit, voucher_type, voucher_no, party}]
 */
async function fetchGLEntries (startDate, endDate, accounts = [], limit = 5000) {
  const filters = [
    ['posting_date', '>=', startDate],
    ['posting_date', '<=', endDate],
    ['is_cancelled', '=', 0],
  ]
  if (accounts.length) {
    filters.push(['account', 'in', accounts])
  }
  const fields = ['posting_date', 'account', 'debit', 'credit', 'voucher_type', 'voucher_no', 'party', 'against_voucher']
  const params = new URLSearchParams({
    fields: JSON.stringify(fields),
    filters: JSON.stringify(filters),
    limit_page_length: limit,
    order_by: 'posting_date asc',
  })
  const res = await erpFetch('/api/resource/GL Entry?' + params)
  if (res.status !== 200) return []
  return res.data?.data || []
}

/**
 * Fetch accounts from Chart of Accounts matching a range or pattern.
 */
async function fetchAccounts (rootType, parentAccount) {
  const filters = []
  if (rootType) filters.push(['root_type', '=', rootType])
  if (parentAccount) filters.push(['parent_account', 'like', '%' + parentAccount + '%'])
  const params = new URLSearchParams({
    fields: JSON.stringify(['name', 'account_name', 'account_number', 'root_type', 'parent_account', 'is_group']),
    filters: JSON.stringify(filters),
    limit_page_length: 200,
    order_by: 'account_number asc, name asc',
  })
  const res = await erpFetch('/api/resource/Account?' + params)
  if (res.status !== 200) return []
  return res.data?.data || []
}

/**
 * Fetch Sales Invoices with items and taxes for the sales report.
 */
async function fetchSalesInvoices (startDate, endDate, limit = 2000) {
  const filters = [
    ['posting_date', '>=', startDate],
    ['posting_date', '<=', endDate],
    ['docstatus', '=', 1], // submitted only
  ]
  const fields = [
    'name', 'posting_date', 'customer', 'customer_name',
    'net_total', 'total_taxes_and_charges', 'grand_total',
    'outstanding_amount', 'status', 'is_return',
  ]
  const params = new URLSearchParams({
    fields: JSON.stringify(fields),
    filters: JSON.stringify(filters),
    limit_page_length: limit,
    order_by: 'posting_date asc',
  })
  const res = await erpFetch('/api/resource/Sales Invoice?' + params)
  if (res.status !== 200) return []
  return res.data?.data || []
}

/**
 * Fetch tax breakdown for a set of invoices.
 */
async function fetchInvoiceTaxes (invoiceNames) {
  if (!invoiceNames.length) return []
  // Fetch in chunks of 100
  const results = []
  for (let i = 0; i < invoiceNames.length; i += 100) {
    const chunk = invoiceNames.slice(i, i + 100)
    const params = new URLSearchParams({
      fields: JSON.stringify(['parent', 'account_head', 'tax_amount', 'description']),
      filters: JSON.stringify([['parent', 'in', chunk], ['parenttype', '=', 'Sales Invoice']]),
      limit_page_length: chunk.length * 5,
    })
    const res = await erpFetch('/api/resource/Sales Taxes and Charges?' + params)
    if (res.status === 200 && res.data?.data) results.push(...res.data.data)
  }
  return results
}

/**
 * Fetch ALL GL entries for a date range + accounts, paginating in chunks.
 * ERPNext REST API limits to ~100k per request; we loop until exhausted.
 */
async function fetchAllGLEntries (startDate, endDate, accounts = []) {
  const PAGE = 50000
  let all = []
  let offset = 0
  while (true) {
    const filters = [
      ['posting_date', '>=', startDate],
      ['posting_date', '<=', endDate],
      ['is_cancelled', '=', 0],
    ]
    if (accounts.length) filters.push(['account', 'in', accounts])
    const fields = ['posting_date', 'account', 'debit', 'credit']
    const params = new URLSearchParams({
      fields: JSON.stringify(fields),
      filters: JSON.stringify(filters),
      limit_page_length: PAGE,
      limit_start: offset,
      order_by: 'posting_date asc',
    })
    const res = await erpFetch('/api/resource/GL Entry?' + params)
    const rows = res.status === 200 ? (res.data?.data || []) : []
    all.push(...rows)
    if (rows.length < PAGE) break
    offset += PAGE
    log(`  GL fetch: ${all.length} entries so far...`)
  }
  return all
}

// ── Report endpoints ──────────────────────────────────────────────────────

/**
 * GET /reports/revenue?start=2026-01-01&end=2026-12-31&mode=gl|items
 * Revenue by GL account per month (mirrors report_revenu.php)
 * mode=gl (default): aggregate GL entries by Income account
 * mode=items: aggregate Sales Invoice Items by item_group (useful when GL accounts not detailed)
 * filter=account1,account2: only show selected accounts (optional)
 */
async function handleRevenue (req, res, url) {
  const start = url.searchParams.get('start')
  const end = url.searchParams.get('end')
  const mode = url.searchParams.get('mode') || 'gl'
  const filterParam = url.searchParams.get('filter') || ''
  if (!start || !end) return json(res, 400, { error: 'start and end dates required (YYYY-MM-DD)' })

  try {
    if (mode === 'items') {
      return await handleRevenueByItems(res, start, end, filterParam)
    }
    return await handleRevenueByGL(res, start, end, filterParam)
  } catch (e) {
    log('Revenue report error:', e.message)
    return json(res, 500, { error: e.message })
  }
}

async function handleRevenueByGL (res, start, end, filterParam) {
  // 1. Get all Income accounts
  const accounts = await fetchAccounts('Income')
  const filterList = filterParam ? filterParam.split(',').map(s => s.trim()) : []
  const filteredAccounts = filterList.length
    ? accounts.filter(a => filterList.includes(a.name) || filterList.includes(a.account_name) || filterList.includes(a.account_number))
    : accounts
  const accountNames = filteredAccounts.map(a => a.name)
  if (!accountNames.length) return json(res, 200, { accounts: [], months: [], all_accounts: accounts.filter(a => !a.is_group).map(a => ({ name: a.name, label: a.account_name, number: a.account_number })) })

  // 2. Fetch GL entries (paginate to handle large datasets)
  const entries = await fetchAllGLEntries(start, end, accountNames)

  // 3. Aggregate by account + month
  const data = {}
  const monthSet = new Set()
  for (const e of entries) {
    const month = e.posting_date.slice(0, 7)
    monthSet.add(month)
    if (!data[e.account]) data[e.account] = {}
    if (!data[e.account][month]) data[e.account][month] = 0
    data[e.account][month] += (e.credit || 0) - (e.debit || 0)
  }

  const months = [...monthSet].sort()
  const accountData = filteredAccounts
    .filter(a => data[a.name])
    .map(a => ({
      name: a.name,
      label: a.account_name || a.name,
      number: a.account_number || '',
      monthly: months.map(m => Math.round((data[a.name][m] || 0) * 100) / 100),
      total: Math.round(Object.values(data[a.name]).reduce((s, v) => s + v, 0) * 100) / 100,
    }))
    .sort((a, b) => (b.total - a.total))

  return json(res, 200, {
    accounts: accountData,
    months,
    mode: 'gl',
    all_accounts: accounts.filter(a => !a.is_group).map(a => ({ name: a.name, label: a.account_name, number: a.account_number })),
  })
}

async function handleRevenueByItems (res, start, end, filterParam) {
  // Fetch Sales Invoice Items grouped by item_group
  const filters = [
    ['posting_date', '>=', start],
    ['posting_date', '<=', end],
    ['docstatus', '=', 1],
  ]
  const params = new URLSearchParams({
    fields: JSON.stringify(['posting_date', 'item_group', 'base_net_amount']),
    filters: JSON.stringify(filters),
    limit_page_length: 50000,
    order_by: 'posting_date asc',
  })
  const r = await erpFetch('/api/resource/Sales Invoice Item?' + params)
  const items = r.status === 200 ? (r.data?.data || []) : []

  const filterList = filterParam ? filterParam.split(',').map(s => s.trim()) : []

  // Aggregate by item_group + month
  const data = {}
  const monthSet = new Set()
  const groupSet = new Set()

  for (const item of items) {
    const group = item.item_group || 'Sans catégorie'
    if (filterList.length && !filterList.includes(group)) continue
    const month = (item.posting_date || '').slice(0, 7)
    if (!month) continue
    monthSet.add(month)
    groupSet.add(group)
    if (!data[group]) data[group] = {}
    if (!data[group][month]) data[group][month] = 0
    data[group][month] += item.base_net_amount || 0
  }

  const months = [...monthSet].sort()
  const groups = [...groupSet]
  const accountData = groups
    .map(g => ({
      name: g,
      label: g,
      number: '',
      monthly: months.map(m => Math.round((data[g][m] || 0) * 100) / 100),
      total: Math.round(Object.values(data[g]).reduce((s, v) => s + v, 0) * 100) / 100,
    }))
    .sort((a, b) => (b.total - a.total))

  return json(res, 200, {
    accounts: accountData,
    months,
    mode: 'items',
    all_accounts: groups.map(g => ({ name: g, label: g, number: '' })),
  })
}

/**
 * GET /reports/sales?start=2026-01-01&end=2026-01-31
 * Sales detail with tax breakdown (mirrors report_vente.php)
 */
async function handleSales (req, res, url) {
  const start = url.searchParams.get('start')
  const end = url.searchParams.get('end')
  if (!start || !end) return json(res, 400, { error: 'start and end dates required' })

  try {
    // 1. Fetch invoices
    const invoices = await fetchSalesInvoices(start, end)

    // 2. Fetch taxes for all invoices
    const names = invoices.map(i => i.name)
    const taxes = await fetchInvoiceTaxes(names)

    // 3. Build tax map: { invoiceName: { TPS: amount, TVQ: amount } }
    const taxMap = {}
    for (const t of taxes) {
      if (!taxMap[t.parent]) taxMap[t.parent] = {}
      // Detect TPS vs TVQ by account head name
      const key = (t.account_head || '').toLowerCase().includes('tps') ? 'TPS'
        : (t.account_head || '').toLowerCase().includes('tvq') ? 'TVQ'
        : (t.description || '').toLowerCase().includes('tps') ? 'TPS'
        : (t.description || '').toLowerCase().includes('tvq') ? 'TVQ'
        : 'Autre'
      taxMap[t.parent][key] = (taxMap[t.parent][key] || 0) + (t.tax_amount || 0)
    }

    // 4. Compose result
    const rows = invoices.map(inv => ({
      name: inv.name,
      date: inv.posting_date,
      customer: inv.customer,
      customer_name: inv.customer_name,
      subtotal: inv.net_total,
      tps: taxMap[inv.name]?.TPS || 0,
      tvq: taxMap[inv.name]?.TVQ || 0,
      total: inv.grand_total,
      outstanding: inv.outstanding_amount,
      status: inv.status,
      is_return: inv.is_return,
    }))

    // 5. Summary
    const summary = rows.reduce((s, r) => {
      s.subtotal += r.subtotal
      s.tps += r.tps
      s.tvq += r.tvq
      s.total += r.total
      s.count++
      if (r.is_return) s.returns++
      return s
    }, { subtotal: 0, tps: 0, tvq: 0, total: 0, count: 0, returns: 0 })

    return json(res, 200, { rows, summary })
  } catch (e) {
    log('Sales report error:', e.message)
    return json(res, 500, { error: e.message })
  }
}

/**
 * GET /reports/taxes?start=2026-01-01&end=2026-03-31&period=monthly|quarterly
 * Tax report: TPS/TVQ collected vs paid (mirrors rapport_tax.php)
 */
async function handleTaxes (req, res, url) {
  const start = url.searchParams.get('start')
  const end = url.searchParams.get('end')
  const period = url.searchParams.get('period') || 'monthly' // monthly | quarterly
  if (!start || !end) return json(res, 400, { error: 'start and end dates required' })

  try {
    // Get all accounts to find tax accounts
    const allAccounts = await fetchAccounts()

    // Find TPS/TVQ accounts by name pattern or account number
    // Legacy: 2300=TPS perçue, 2305=TPS payée, 2350=TVQ perçue, 2355=TVQ payée
    const matchAny = (a, keywords) => {
      const name = (a.account_name || '').toLowerCase()
      const num = a.account_number || ''
      return keywords.some(k => name.includes(k) || num === k)
    }

    const tpsCollected = allAccounts.filter(a =>
      matchAny(a, ['2300']) ||
      (matchAny(a, ['tps']) && matchAny(a, ['perçue', 'percue', 'collect', 'à payer']))
    ).map(a => a.name)

    const tpsPaid = allAccounts.filter(a =>
      matchAny(a, ['2305']) ||
      (matchAny(a, ['tps']) && matchAny(a, ['payée', 'payee', 'paid', 'input']))
    ).map(a => a.name)

    const tvqCollected = allAccounts.filter(a =>
      matchAny(a, ['2350']) ||
      (matchAny(a, ['tvq']) && matchAny(a, ['perçue', 'percue', 'collect', 'à payer']))
    ).map(a => a.name)

    const tvqPaid = allAccounts.filter(a =>
      matchAny(a, ['2355']) ||
      (matchAny(a, ['tvq']) && matchAny(a, ['payée', 'payee', 'paid', 'input']))
    ).map(a => a.name)

    // Also find Income accounts for revenue total
    const incomeAccounts = allAccounts
      .filter(a => a.root_type === 'Income')
      .map(a => a.name)

    // Fetch all relevant GL entries
    const allRelevant = [...tpsCollected, ...tpsPaid, ...tvqCollected, ...tvqPaid, ...incomeAccounts]
    const entries = await fetchGLEntries(start, end, allRelevant, 10000)

    // Generate period buckets
    const buckets = generatePeriodBuckets(start, end, period)

    // Aggregate per bucket
    const result = buckets.map(bucket => {
      const bucketEntries = entries.filter(e =>
        e.posting_date >= bucket.start && e.posting_date <= bucket.end
      )

      const sumByAccounts = (accts, creditPositive = true) => {
        return bucketEntries
          .filter(e => accts.includes(e.account))
          .reduce((sum, e) => sum + (creditPositive ? (e.credit - e.debit) : (e.debit - e.credit)), 0)
      }

      return {
        label: bucket.label,
        start: bucket.start,
        end: bucket.end,
        tps_collected: Math.round(sumByAccounts(tpsCollected) * 100) / 100,
        tps_paid: Math.round(sumByAccounts(tpsPaid, false) * 100) / 100,
        tvq_collected: Math.round(sumByAccounts(tvqCollected) * 100) / 100,
        tvq_paid: Math.round(sumByAccounts(tvqPaid, false) * 100) / 100,
        revenue: Math.round(sumByAccounts(incomeAccounts) * 100) / 100,
      }
    })

    // Add net amounts
    for (const r of result) {
      r.tps_net = Math.round((r.tps_collected - r.tps_paid) * 100) / 100
      r.tvq_net = Math.round((r.tvq_collected - r.tvq_paid) * 100) / 100
      r.total_tax_net = Math.round((r.tps_net + r.tvq_net) * 100) / 100
    }

    return json(res, 200, {
      periods: result,
      accounts: {
        tps_collected: tpsCollected,
        tps_paid: tpsPaid,
        tvq_collected: tvqCollected,
        tvq_paid: tvqPaid,
        income: incomeAccounts.length,
      },
    })
  } catch (e) {
    log('Tax report error:', e.message)
    return json(res, 500, { error: e.message })
  }
}

/**
 * GET /reports/accounts-receivable?as_of=2026-04-01
 * Aging report (mirrors rapport_age_compte.php)
 */
async function handleAR (req, res, url) {
  const asOf = url.searchParams.get('as_of') || new Date().toISOString().slice(0, 10)

  try {
    // Fetch unpaid invoices
    const filters = [
      ['docstatus', '=', 1],
      ['outstanding_amount', '!=', 0],
      ['posting_date', '<=', asOf],
    ]
    const fields = [
      'name', 'posting_date', 'due_date', 'customer', 'customer_name',
      'grand_total', 'outstanding_amount',
    ]
    const params = new URLSearchParams({
      fields: JSON.stringify(fields),
      filters: JSON.stringify(filters),
      limit_page_length: 5000,
      order_by: 'posting_date asc',
    })
    const r = await erpFetch('/api/resource/Sales Invoice?' + params)
    const invoices = r.status === 200 ? (r.data?.data || []) : []

    const asOfDate = new Date(asOf)
    const buckets = { current: 0, d30: 0, d60: 0, d90: 0, d120: 0 }
    const customers = {}

    for (const inv of invoices) {
      const due = new Date(inv.due_date || inv.posting_date)
      const daysOverdue = Math.floor((asOfDate - due) / 86400000)
      const amt = inv.outstanding_amount || 0
      const bucket = daysOverdue <= 0 ? 'current'
        : daysOverdue <= 30 ? 'd30'
        : daysOverdue <= 60 ? 'd60'
        : daysOverdue <= 90 ? 'd90' : 'd120'

      buckets[bucket] += amt

      if (!customers[inv.customer]) {
        customers[inv.customer] = {
          customer: inv.customer,
          customer_name: inv.customer_name,
          current: 0, d30: 0, d60: 0, d90: 0, d120: 0, total: 0,
        }
      }
      customers[inv.customer][bucket] += amt
      customers[inv.customer].total += amt
    }

    const rows = Object.values(customers).sort((a, b) => b.total - a.total)
    const total = Object.values(buckets).reduce((s, v) => s + v, 0)

    return json(res, 200, {
      as_of: asOf,
      summary: { ...buckets, total: Math.round(total * 100) / 100 },
      rows: rows.map(r => ({
        ...r,
        current: Math.round(r.current * 100) / 100,
        d30: Math.round(r.d30 * 100) / 100,
        d60: Math.round(r.d60 * 100) / 100,
        d90: Math.round(r.d90 * 100) / 100,
        d120: Math.round(r.d120 * 100) / 100,
        total: Math.round(r.total * 100) / 100,
      })),
      invoice_count: invoices.length,
    })
  } catch (e) {
    log('AR report error:', e.message)
    return json(res, 500, { error: e.message })
  }
}

// ── Helpers ───────────────────────────────────────────────────────────────

function generatePeriodBuckets (startStr, endStr, period) {
  const buckets = []
  const start = new Date(startStr + 'T00:00:00')
  const end = new Date(endStr + 'T23:59:59')
  const FR_MONTHS = ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre']

  if (period === 'quarterly') {
    let cur = new Date(start.getFullYear(), Math.floor(start.getMonth() / 3) * 3, 1)
    while (cur <= end) {
      const qEnd = new Date(cur.getFullYear(), cur.getMonth() + 3, 0) // last day of quarter
      const Q = Math.floor(cur.getMonth() / 3) + 1
      buckets.push({
        label: `T${Q} ${cur.getFullYear()}`,
        start: cur.toISOString().slice(0, 10),
        end: (qEnd > end ? end : qEnd).toISOString().slice(0, 10),
      })
      cur = new Date(cur.getFullYear(), cur.getMonth() + 3, 1)
    }
  } else {
    let cur = new Date(start.getFullYear(), start.getMonth(), 1)
    while (cur <= end) {
      const mEnd = new Date(cur.getFullYear(), cur.getMonth() + 1, 0)
      buckets.push({
        label: FR_MONTHS[cur.getMonth()] + ' ' + cur.getFullYear(),
        start: cur.toISOString().slice(0, 10),
        end: (mEnd > end ? end : mEnd).toISOString().slice(0, 10),
      })
      cur = new Date(cur.getFullYear(), cur.getMonth() + 1, 1)
    }
  }
  return buckets
}

// ── Route handler ─────────────────────────────────────────────────────────

async function handle (req, res, method, path, url) {
  if (method !== 'GET') return json(res, 405, { error: 'GET only' })

  if (path === '/reports/revenue') return handleRevenue(req, res, url)
  if (path === '/reports/sales') return handleSales(req, res, url)
  if (path === '/reports/taxes') return handleTaxes(req, res, url)
  if (path === '/reports/ar') return handleAR(req, res, url)
  if (path === '/reports/accounts') {
    const type = url.searchParams.get('type') || 'Income'
    const accounts = await fetchAccounts(type)
    return json(res, 200, { accounts })
  }

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

module.exports = { handle }
