/**
 * Step-handlers для reports-stats.
 *
 * Источник правды по выручке — GET /api/sales/retail/stats. Проверяем что
 * агрегаты считаются только по Posted-чекам, серия по дням непрерывна и
 * длиной ровно `days`, и что данные строго tenant-scoped (орг A ≠ орг B).
 * Профит по себестоимости и ABC-анализ пока не реализованы — фиксируем как
 * Logic gaps.
 */
import { login, makeClient } from '../lib/api.js'
import { generateEan13 } from '../lib/barcode.js'
import type { CheckResult, Step, Report } from '../lib/report.js'
import type { AxiosInstance } from 'axios'

const TS = Date.now()

interface OrgBundle {
  orgId: string
  token: string
  productId: string
  storeId: string
  retailPointId: string
  currencyId: string
}
interface Ctx {
  apiOnly: boolean
  superAdminToken?: string
  a?: OrgBundle
  b?: OrgBundle
  revenueA?: number
}
interface StepCtx { ctx: Ctx; step: Step; report: Report }

function check(step: Step, c: CheckResult) { step.checks.push(c) }
function asString(x: unknown): string {
  if (x == null) return ''
  if (typeof x === 'string') return x
  try { return JSON.stringify(x).slice(0, 200) } catch { return String(x) }
}
const approx = (a: number, b: number) => Math.abs(a - b) < 0.01

async function ensureSuperAdmin(ctx: Ctx): Promise<string> {
  if (!ctx.superAdminToken) {
    const sa = await login('admin@food-market.local', 'Admin12345!')
    ctx.superAdminToken = sa.accessToken
  }
  return ctx.superAdminToken
}

// Полный bootstrap отдельной организации: товар + склад + точка + поставщик +
// приёмка на 100 ед (чтобы было что продавать).
async function bootstrapOrg(saToken: string, label: string): Promise<OrgBundle | undefined> {
  const orgRes = await makeClient(saToken).post('/api/super-admin/organizations', {
    org: {
      name: `Reports ${label} ${TS}`, countryCode: 'KZ',
      bin: null, address: null, phone: null, email: null,
      defaultCurrencyId: null, accountOwnerUserId: null,
    },
    adminLastName: label, adminFirstName: 'Admin',
    adminEmail: `reports-${label.toLowerCase()}-${TS}@example.kz`, adminPosition: null,
  })
  if (orgRes.status !== 200) return undefined
  const sess = await login(orgRes.data.adminEmail, orgRes.data.adminTempPassword)
  const api = makeClient(sess.accessToken)

  const units = await api.get('/api/catalog/units-of-measure')
  const unitId = units.data?.items?.[0]?.id ?? units.data?.[0]?.id
  const grps = await api.get('/api/catalog/product-groups?pageSize=10')
  let groupId = grps.data?.items?.[0]?.id
  if (!groupId) groupId = (await api.post('/api/catalog/product-groups', { name: 'G', parentId: null })).data?.id
  const cur = await api.get('/api/catalog/currencies?pageSize=200')
  const currencyId = cur.data?.items?.find((c: { code: string }) => c.code === 'KZT')?.id
  const pt = await api.get('/api/catalog/price-types')
  const retailPriceTypeId = pt.data?.items?.find((p: { isRetail: boolean }) => p.isRetail)?.id
  const storeId = (await api.get('/api/catalog/stores?pageSize=10')).data?.items?.[0]?.id
  const retailPointId = (await api.get('/api/catalog/retail-points?pageSize=10')).data?.items?.[0]?.id
  const supplierId = (await api.post('/api/catalog/counterparties', {
    name: `Sup ${label} ${TS}`, type: 1, bin: '987654321012', phone: '+77001112233',
  })).data?.id
  const productId = (await api.post('/api/catalog/products', {
    name: `Prod ${label} ${TS}`, unitOfMeasureId: unitId, productGroupId: groupId,
    vat: 12, vatEnabled: true,
    barcodes: [{ code: generateEan13(), type: 1, isPrimary: true }],
    prices: [{ priceTypeId: retailPriceTypeId, currencyId, amount: 200 }],
  })).data?.id

  // Приёмка 100 ед, проводим.
  const sup = await api.post('/api/purchases/supplies', {
    storeId, supplierId, currencyId, date: new Date().toISOString(),
    lines: [{ productId, quantity: 100, unitPrice: 100 }],
  })
  if (sup.status === 201) await api.post(`/api/purchases/supplies/${sup.data.id}/post`)

  return { orgId: orgRes.data.organization.id, token: sess.accessToken, productId, storeId, retailPointId, currencyId }
}

// Проводит чек и возвращает его Total (из ответа Create).
async function postSale(b: OrgBundle, quantity: number, unitPrice: number): Promise<number | undefined> {
  const api = makeClient(b.token)
  const sale = await api.post('/api/sales/retail', {
    storeId: b.storeId, retailPointId: b.retailPointId, currencyId: b.currencyId,
    date: new Date().toISOString(),
    lines: [{ productId: b.productId, quantity, unitPrice, vatPercent: 12 }],
    paidCash: quantity * unitPrice * 2, paidCard: 0,
  })
  if (sale.status !== 201) return undefined
  const total = Number(sale.data.total)
  const p = await api.post(`/api/sales/retail/${sale.data.id}/post`)
  return p.status >= 400 ? undefined : total
}

async function getStats(token: string, days?: number) {
  const api: AxiosInstance = makeClient(token)
  return api.get(`/api/sales/retail/stats${days ? `?days=${days}` : ''}`)
}

// ---------------------------------------------------------------------------

export async function step01_bootstrap({ ctx, step, report }: StepCtx) {
  const sa = await ensureSuperAdmin(ctx)
  ctx.a = await bootstrapOrg(sa, 'A')
  check(step, { kind: 'api', description: 'Орг A с товаром и остатком готова',
    ok: !!ctx.a?.productId, detail: ctx.a ? asString(ctx.a.orgId) : 'bootstrap failed' })
  if (!ctx.a) report.bug({ step: '01', severity: 'critical', title: 'Bootstrap орг A не удался', detail: '' })
}

export async function step02_stats_reflect_posted_sales({ ctx, step, report }: StepCtx) {
  if (!ctx.a) { step.status = 'skip'; return }
  const totals: number[] = []
  for (const [q, p] of [[3, 200], [2, 250], [1, 400]] as const) {
    const t = await postSale(ctx.a, q, p)
    if (t !== undefined) totals.push(t)
  }
  check(step, { kind: 'api', description: 'Проведено 3 чека', ok: totals.length === 3, detail: `totals=${totals.join(',')}` })
  const expected = totals.reduce((a, b) => a + b, 0)
  ctx.revenueA = expected

  const r = await getStats(ctx.a.token)
  check(step, { kind: 'api', description: 'GET stats → 200', ok: r.status === 200, detail: `status=${r.status}` })
  if (r.status !== 200) return
  const d = r.data
  check(step, { kind: 'api', description: 'RevenueToday == Σ проведённых чеков',
    ok: approx(Number(d.revenueToday), expected), detail: `today=${d.revenueToday} expected=${expected}` })
  check(step, { kind: 'api', description: 'TransactionsToday == 3',
    ok: d.transactionsToday === 3, detail: `tx=${d.transactionsToday}` })
  check(step, { kind: 'api', description: 'RevenueThisMonth == RevenueToday (все чеки сегодня)',
    ok: approx(Number(d.revenueThisMonth), expected), detail: `month=${d.revenueThisMonth}` })
  check(step, { kind: 'api', description: 'AvgTicketThisMonth == Revenue/Transactions',
    ok: approx(Number(d.avgTicketThisMonth), expected / 3), detail: `avg=${d.avgTicketThisMonth} expected=${(expected / 3).toFixed(2)}` })
  // Серия по дням: непрерывна, длина = 30 (default), последний бакет = сегодня.
  const series = d.series ?? []
  check(step, { kind: 'api', description: 'Серия длиной 30 дней (default)',
    ok: series.length === 30, detail: `len=${series.length}` })
  const last = series[series.length - 1]
  check(step, { kind: 'api', description: 'Последний бакет серии (сегодня) == RevenueToday',
    ok: !!last && approx(Number(last.revenue), expected) && last.transactions === 3,
    detail: `last={rev:${last?.revenue},tx:${last?.transactions}}` })
}

export async function step03_draft_sale_excluded({ ctx, step }: StepCtx) {
  if (!ctx.a) { step.status = 'skip'; return }
  const api = makeClient(ctx.a.token)
  // Черновик: создаём, но НЕ проводим.
  const draft = await api.post('/api/sales/retail', {
    storeId: ctx.a.storeId, retailPointId: ctx.a.retailPointId, currencyId: ctx.a.currencyId,
    date: new Date().toISOString(),
    lines: [{ productId: ctx.a.productId, quantity: 5, unitPrice: 999, vatPercent: 12 }],
    paidCash: 0, paidCard: 0,
  })
  check(step, { kind: 'api', description: 'Черновик чека создан (Status=Draft)',
    ok: draft.status === 201, detail: `status=${draft.status}` })

  const r = await getStats(ctx.a.token)
  check(step, { kind: 'api', description: 'RevenueToday не изменился (черновик исключён)',
    ok: approx(Number(r.data.revenueToday), ctx.revenueA ?? -1), detail: `today=${r.data.revenueToday} expected=${ctx.revenueA}` })
  check(step, { kind: 'api', description: 'TransactionsToday остался 3',
    ok: r.data.transactionsToday === 3, detail: `tx=${r.data.transactionsToday}` })
}

export async function step04_stats_tenant_isolated({ ctx, step, report }: StepCtx) {
  if (!ctx.a) { step.status = 'skip'; return }
  const sa = await ensureSuperAdmin(ctx)
  ctx.b = await bootstrapOrg(sa, 'B')
  check(step, { kind: 'api', description: 'Орг B готова', ok: !!ctx.b?.productId })
  if (!ctx.b) return

  const bTotal = await postSale(ctx.b, 4, 150)
  check(step, { kind: 'api', description: 'В орг B проведён 1 чек', ok: bTotal !== undefined, detail: `total=${bTotal}` })

  // Орг A — без изменений.
  const ra = await getStats(ctx.a.token)
  check(step, { kind: 'api', description: 'stats орг A не включает продажу орг B',
    ok: approx(Number(ra.data.revenueToday), ctx.revenueA ?? -1) && ra.data.transactionsToday === 3,
    detail: `A.today=${ra.data.revenueToday} A.tx=${ra.data.transactionsToday}` })
  if (!approx(Number(ra.data.revenueToday), ctx.revenueA ?? -1) || ra.data.transactionsToday !== 3) {
    report.bug({ step: '04', severity: 'critical', title: 'Утечка выручки между тенантами в /stats',
      detail: `Орг A видит чужие продажи: today=${ra.data.revenueToday}, tx=${ra.data.transactionsToday}` })
  }
  // Орг B — видит только свой чек.
  const rb = await getStats(ctx.b.token)
  check(step, { kind: 'api', description: 'stats орг B == только её 1 чек',
    ok: approx(Number(rb.data.revenueToday), Number(bTotal)) && rb.data.transactionsToday === 1,
    detail: `B.today=${rb.data.revenueToday} B.tx=${rb.data.transactionsToday}` })
}

export async function step05_days_param_and_gaps({ ctx, step, report }: StepCtx) {
  if (!ctx.a) { step.status = 'skip'; return }
  const r7 = await getStats(ctx.a.token, 7)
  check(step, { kind: 'api', description: 'days=7 → серия длиной 7',
    ok: (r7.data.series?.length ?? 0) === 7, detail: `len=${r7.data.series?.length}` })

  // Профит/ABC/остатки-на-дату/экспорт (ТЗ 2.12) — не реализованы. Проверяем,
  // что отдельного /api/reports нет, и фиксируем как gap, а не баг.
  const api = makeClient(ctx.a.token)
  const profit = await api.get('/api/reports/profit')
  const sales = await api.get('/api/reports/sales')
  check(step, { kind: 'api', description: 'Отчётов /api/reports/* нет (ожидаемо 404)',
    ok: profit.status === 404 && sales.status === 404, detail: `profit=${profit.status} sales=${sales.status}` })

  report.gap('ТЗ 2.12: отчёт «прибыль» (выручка − себестоимость по Cost-снимку RetailSaleLine) не реализован — RetailSaleLine не хранит снимок себестоимости, /stats отдаёт только валовую выручку.')
  report.gap('ТЗ 2.12: ABC-анализ, «остатки на дату» (SUM Movement до даты) и экспорт CSV/XLSX отсутствуют — отдельного ReportsController нет.')
}
