/**
 * Stage reports: Sales/Stock/Profit/ABC + export + edge.
 */
import { login, makeClient } from '../lib/api.js'
import type { CheckResult, Step, Report } from '../lib/report.js'
import { generateEan13 } from '../lib/barcode.js'

type Org = {
  orgId: string; email: string; password: string; token: string
  storeId: string; retailPointId: string; productId: string
  currencyId: string; unitKgId: string; groupId: string; priceTypeRetailId: string
  saleId?: string
}
type Ctx = {
  apiOnly: boolean
  ts: number
  orgA?: Org
}
interface StepCtx { ctx: Ctx; step: Step; report: Report }

const TS = Date.now()
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
  return JSON.stringify(x)
}

async function bootstrapOrg(suffix: string, phone: string, bcIdx: number): Promise<Org> {
  const api = makeClient()
  const email = `stage-rep-${suffix}-${TS}@food-market.local`
  const password = 'StageRep12345!'
  let r = await api.post('/api/auth/signup', { email, password, organizationName: `Rep ${suffix} ${TS}`, phone, plan: 'start' })
  for (let i = 0; i < 5 && r.status === 429; i++) {
    await new Promise(res => setTimeout(res, 15000))
    r = await api.post('/api/auth/signup', { email, password, organizationName: `Rep ${suffix} ${TS}`, phone, plan: 'start' })
  }
  if (r.status !== 200) throw new Error(`signup ${suffix}: ${r.status} ${JSON.stringify(r.data)}`)
  const sess = await login(email, password)
  const auth = makeClient(sess.accessToken)
  const [stores, units, groups, prices, currencies, points] = await Promise.all([
    auth.get('/api/catalog/stores'),
    auth.get('/api/catalog/units-of-measure'),
    auth.get('/api/catalog/product-groups'),
    auth.get('/api/catalog/price-types'),
    auth.get('/api/catalog/currencies'),
    auth.get('/api/catalog/retail-points'),
  ])
  const storeId = stores.data.items.find((s: { isMain: boolean }) => s.isMain).id
  const retailPointId = points.data.items[0]?.id
  const unitKgId = units.data.items.find((u: { code: string }) => u.code === '166').id
  const groupId = groups.data.items.find((g: { parentId: string | null }) => g.parentId == null).id
  const priceTypeRetailId = prices.data.items.find((p: { isRequired: boolean }) => p.isRequired).id
  const currencyId = currencies.data.items.find((c: { code: string }) => c.code === 'KZT').id

  const prod = await auth.post('/api/catalog/products', {
    name: `Rep Prod ${suffix} ${TS}`, article: `R-${suffix}-${TS}`,
    unitOfMeasureId: unitKgId, vat: 0, vatEnabled: false, productGroupId: groupId,
    barcodes: [{ code: generateEan13(bcIdx), type: 1, isPrimary: true }],
    prices: [{ priceTypeId: priceTypeRetailId, amount: 500, currencyId }],
  })
  if (prod.status !== 201) throw new Error(`prod ${suffix}: ${prod.status} ${JSON.stringify(prod.data)}`)

  return {
    orgId: r.data.organizationId, email, password, token: sess.accessToken,
    storeId, retailPointId, productId: prod.data.id,
    currencyId, unitKgId, groupId, priceTypeRetailId,
  }
}

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

export async function rep01_setup({ ctx, step, report }: StepCtx) {
  ctx.ts = TS
  ctx.orgA = await bootstrapOrg('a', '+77011180001', 81)
  const a = ctx.orgA
  const api = makeClient(a.token)

  // Enter 100 @ 30
  const enter = await api.post('/api/inventory/enters', {
    date: new Date().toISOString(), storeId: a.storeId, currencyId: a.currencyId,
    lines: [{ productId: a.productId, quantity: 100, unitCost: 30 }],
  })
  await api.post(`/api/inventory/enters/${enter.data.id}/post`, {})

  // Sale 10 @ 500
  const sale = await api.post('/api/sales/retail', {
    date: new Date().toISOString(),
    storeId: a.storeId, retailPointId: a.retailPointId,
    currencyId: a.currencyId, customerId: null, payment: 0,
    paidCash: 5000, paidCard: 0, notes: 'rep test',
    isReturn: false, referenceSaleId: null,
    lines: [{ productId: a.productId, quantity: 10, unitPrice: 500, discount: 0, vatPercent: 0 }],
  })
  await api.post(`/api/sales/retail/${sale.data.id}/post`, {})
  a.saleId = sale.data.id

  // Loss 5 @ 30
  const loss = await api.post('/api/inventory/losses', {
    date: new Date().toISOString(), storeId: a.storeId, currencyId: a.currencyId,
    reason: 0, lines: [{ productId: a.productId, quantity: 5, unitCost: 30 }],
  })
  await api.post(`/api/inventory/losses/${loss.data.id}/post`, {})

  check(step, { kind: 'api', description: 'Operations applied (enter+sale+loss)', ok: true, detail: 'qty=100→90→85' })
}

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

export async function rep02_sales_report({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const today = new Date().toISOString().slice(0, 10)
  const from = today
  const to = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString().slice(0, 10)
  const res = await api.get(`/api/reports/sales?from=${from}&to=${to}&groupBy=period:day`)
  check(step, { kind: 'api', description: 'GET /reports/sales → 200', ok: res.status === 200, detail: `${res.status}` })
  const rows = res.data ?? []
  const totalRev = rows.reduce((s: number, r: { revenue: number }) => s + r.revenue, 0)
  check(step, {
    kind: 'api', description: 'Σ revenue = 5000 (10 × 500)',
    ok: Math.abs(totalRev - 5000) < 0.01,
    detail: `revenue=${totalRev}, rows=${rows.length}`,
  })
  const totalTx = rows.reduce((s: number, r: { transactions: number }) => s + r.transactions, 0)
  check(step, { kind: 'api', description: 'transactions ≥ 1', ok: totalTx >= 1, detail: `tx=${totalTx}` })
}

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

export async function rep03_stock_report({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const res = await api.get('/api/reports/stock')
  check(step, { kind: 'api', description: 'GET /reports/stock → 200', ok: res.status === 200, detail: `${res.status}` })
  const ours = (res.data ?? []).find((r: { productId: string }) => r.productId === a.productId)
  check(step, {
    kind: 'api', description: 'Текущий остаток = 85 (100−10−5)',
    ok: ours?.quantity === 85,
    detail: `qty=${ours?.quantity}`,
  })
}

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

export async function rep04_profit_report({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const today = new Date().toISOString().slice(0, 10)
  const to = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString().slice(0, 10)
  const res = await api.get(`/api/reports/profit?from=${today}&to=${to}&groupBy=period:day`)
  check(step, { kind: 'api', description: 'GET /reports/profit → 200', ok: res.status === 200, detail: `${res.status}` })
  const rows = res.data ?? []
  const totalRev = rows.reduce((s: number, r: { revenue: number }) => s + r.revenue, 0)
  const totalCost = rows.reduce((s: number, r: { cost: number }) => s + r.cost, 0)
  check(step, {
    kind: 'api', description: 'revenue=5000, cost=10×30=300, прибыль=4700',
    ok: Math.abs(totalRev - 5000) < 0.01 && Math.abs(totalCost - 300) < 0.01,
    detail: `revenue=${totalRev} cost=${totalCost}`,
  })
}

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

export async function rep05_abc_report({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const today = new Date().toISOString().slice(0, 10)
  const to = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString().slice(0, 10)
  const res = await api.get(`/api/reports/abc?from=${today}&to=${to}&metric=revenue`)
  check(step, { kind: 'api', description: 'GET /reports/abc → 200', ok: res.status === 200, detail: `${res.status}` })
  const rows = res.data ?? []
  const ours = rows.find((r: { productId: string }) => r.productId === a.productId)
  check(step, {
    kind: 'api', description: 'Наш товар = class A, rank=1',
    ok: ours?.abcClass === 'A' && ours?.rank === 1,
    detail: `class=${ours?.abcClass} rank=${ours?.rank}`,
  })
}

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

export async function rep06_export_csv_xlsx({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const today = new Date().toISOString().slice(0, 10)
  const to = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString().slice(0, 10)

  const csv = await api.get(`/api/reports/sales/export?from=${today}&to=${to}&format=csv`)
  check(step, {
    kind: 'api', description: 'CSV export → 200 + text/csv ИЛИ application/octet-stream',
    ok: csv.status === 200 && /csv|text|octet/i.test(String(csv.headers?.['content-type'] ?? '')),
    detail: `${csv.status} ct=${csv.headers?.['content-type']}`,
  })

  const xlsx = await api.get(`/api/reports/sales/export?from=${today}&to=${to}&format=xlsx`)
  check(step, {
    kind: 'api', description: 'XLSX export → 200 + spreadsheet mime',
    ok: xlsx.status === 200 && /sheet|xlsx|excel|octet/i.test(String(xlsx.headers?.['content-type'] ?? '')),
    detail: `${xlsx.status} ct=${xlsx.headers?.['content-type']}`,
  })
}

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

export async function rep07_empty_period({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA) { step.status = 'skip'; return }
  const api = makeClient(ctx.orgA.token)
  const res = await api.get('/api/reports/sales?from=1999-01-01&to=1999-01-02&groupBy=period:day')
  check(step, {
    kind: 'api', description: 'Пустой период → 200, пустой массив или нулевые суммы',
    ok: res.status === 200 && Array.isArray(res.data) && res.data.length === 0,
    detail: `${res.status} len=${res.data?.length}`,
  })

  // Деление на ноль (Profit margin) — если revenue=0, мы ждём что margin не NaN
  const profit = await api.get('/api/reports/profit?from=1999-01-01&to=1999-01-02')
  check(step, {
    kind: 'api', description: 'Пустой profit период → 200, нет NaN',
    ok: profit.status === 200 && !/NaN|null,null/i.test(JSON.stringify(profit.data ?? [])),
    detail: `${profit.status} len=${profit.data?.length}`,
  })
}

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

export async function rep08_store_filter({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const today = new Date().toISOString().slice(0, 10)
  const to = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString().slice(0, 10)
  // Несуществующий storeId
  const random = '00000000-0000-0000-0000-000000000001'
  const res = await api.get(`/api/reports/sales?from=${today}&to=${to}&storeId=${random}`)
  check(step, {
    kind: 'api', description: 'GET /reports/sales?storeId=<несуществ> → 200 + 0 строк',
    ok: res.status === 200 && Array.isArray(res.data) && res.data.length === 0,
    detail: `${res.status} len=${res.data?.length}`,
  })
}
