/**
 * Stage CustomerReturn (через RetailSale IsReturn) — по чеку и без.
 */
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
}
type Ctx = {
  apiOnly: boolean
  ts: number
  orgA?: Org
  orgB?: Org
  saleIdA?: string
  returnIdA?: string
}
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-cr-${suffix}-${TS}@food-market.local`
  const password = 'StageCr12345!'
  let r = await api.post('/api/auth/signup', { email, password, organizationName: `CR ${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: `CR ${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: `CR Prod ${suffix} ${TS}`, article: `CR-${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,
  }
}

async function getStock(token: string, storeId: string, productId: string): Promise<number> {
  const r = await makeClient(token).get(`/api/inventory/stock?storeId=${storeId}`)
  const row = (r.data?.items ?? []).find((x: { productId: string }) => x.productId === productId)
  return row?.quantity ?? 0
}

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

export async function cr01_setup_and_sale({ ctx, step, report }: StepCtx) {
  ctx.ts = TS
  ctx.orgA = await bootstrapOrg('a', '+77011150001', 51)
  await new Promise(r => setTimeout(r, 1500))
  ctx.orgB = await bootstrapOrg('b', '+77011150002', 52)

  const a = ctx.orgA
  const api = makeClient(a.token)
  // Stock=100 через Enter
  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: 200 }],
  })
  await api.post(`/api/inventory/enters/${enter.data.id}/post`, {})

  // Продажа на 10шт
  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: 'sale to return',
    isReturn: false, referenceSaleId: null,
    lines: [{ productId: a.productId, quantity: 10, unitPrice: 500, discount: 0, vatPercent: 0 }],
  })
  check(step, { kind: 'api', description: 'POST RetailSale → 201', ok: sale.status === 201, detail: `${sale.status} ${asString(sale.data).slice(0, 200)}` })
  if (sale.status !== 201) return
  ctx.saleIdA = sale.data.id
  const post = await api.post(`/api/sales/retail/${ctx.saleIdA}/post`, {})
  check(step, { kind: 'api', description: 'Post sale → 204', ok: post.status === 204, detail: `${post.status} ${asString(post.data).slice(0, 200)}` })
  const stock = await getStock(a.token, a.storeId, a.productId)
  check(step, { kind: 'api', description: 'Stock = 90 (100 - 10)', ok: stock === 90, detail: `${stock}` })
}

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

export async function cr02_create_return_from_sale({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA || !ctx.saleIdA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const res = await api.post(`/api/sales/retail/${ctx.saleIdA}/create-return`, {})
  check(step, { kind: 'api', description: 'POST /create-return → 201', ok: res.status === 201, detail: `${res.status} ${asString(res.data).slice(0, 250)}` })
  if (res.status !== 201) return
  ctx.returnIdA = res.data.id
  check(step, { kind: 'api', description: 'isReturn=true, referenceSaleId совпадает', ok: res.data.isReturn === true && res.data.referenceSaleId === ctx.saleIdA, detail: `isReturn=${res.data.isReturn} ref=${res.data.referenceSaleId}` })
  check(step, { kind: 'api', description: 'Status=Draft', ok: res.data.status === 0, detail: `status=${res.data.status}` })
  check(step, { kind: 'api', description: 'Lines.qty=10 (initial = original − returned = 10−0)', ok: res.data.lines?.[0]?.quantity === 10, detail: `qty=${res.data.lines?.[0]?.quantity}` })
}

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

export async function cr03_post_return_stock_back({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA || !ctx.returnIdA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const before = await getStock(a.token, a.storeId, a.productId)
  const post = await api.post(`/api/sales/retail/${ctx.returnIdA}/post`, {})
  check(step, { kind: 'api', description: 'POST return /post → 204', ok: post.status === 204, detail: `${post.status} ${asString(post.data).slice(0, 200)}` })
  const after = await getStock(a.token, a.storeId, a.productId)
  check(step, { kind: 'api', description: 'Stock += 10 (вернулось)', ok: after - before === 10, detail: `${before} → ${after}` })
  const movs = await api.get('/api/inventory/movements?take=200')
  const ours = (movs.data?.items ?? []).filter((m: { documentId: string }) => m.documentId === ctx.returnIdA)
  check(step, {
    kind: 'api', description: 'StockMovement type=CustomerReturn создан',
    ok: ours.length >= 1 && ours[0].type === 'CustomerReturn',
    detail: `count=${ours.length} type=${ours[0]?.type}`,
  })
}

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

export async function cr04_overreturn_blocked({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA || !ctx.saleIdA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  // Уже вернули 10/10. Создадим ещё один возврат — должно либо запретить создание (BadRequest "уже возвращён"), либо если создаст, на post будет 409 over-return.
  const second = await api.post(`/api/sales/retail/${ctx.saleIdA}/create-return`, {})
  if (second.status === 400 && /полност.*возвращ|already/i.test(asString(second.data))) {
    check(step, {
      kind: 'api', description: '/create-return уже после полного возврата → 400',
      ok: true, detail: `${second.status} ${asString(second.data).slice(0, 200)}`,
    })
    return
  }
  // Иначе попробуем сделать ручной возврат на 5 (всё ещё больше remaining=0)
  const manual = await api.post('/api/sales/retail', {
    date: new Date().toISOString(), storeId: a.storeId, retailPointId: a.retailPointId,
    currencyId: a.currencyId, customerId: null, payment: 0,
    paidCash: 0, paidCard: 0, notes: 'over-return test',
    isReturn: true, referenceSaleId: ctx.saleIdA,
    lines: [{ productId: a.productId, quantity: 5, unitPrice: 500, discount: 0, vatPercent: 0 }],
  })
  if (manual.status !== 201) {
    step.notes.push(`manual create unexpected: ${manual.status} ${asString(manual.data).slice(0, 200)}`)
    return
  }
  const post = await api.post(`/api/sales/retail/${manual.data.id}/post`, {})
  check(step, {
    kind: 'api', description: 'POST over-return /post → 409',
    ok: post.status === 409 && /превыш|over|больше|доступн/i.test(asString(post.data)),
    detail: `${post.status} ${asString(post.data).slice(0, 200)}`,
  })
}

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

export async function cr05_return_without_receipt({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const create = await api.post('/api/sales/retail', {
    date: new Date().toISOString(), storeId: a.storeId, retailPointId: a.retailPointId,
    currencyId: a.currencyId, customerId: null, payment: 0,
    paidCash: 0, paidCard: 0, notes: 'walk-in return',
    isReturn: true, referenceSaleId: null,
    lines: [{ productId: a.productId, quantity: 3, unitPrice: 500, discount: 0, vatPercent: 0 }],
  })
  check(step, { kind: 'api', description: 'Walk-in return draft → 201', ok: create.status === 201, detail: `${create.status} ${asString(create.data).slice(0, 200)}` })
  if (create.status !== 201) return
  const before = await getStock(a.token, a.storeId, a.productId)
  const post = await api.post(`/api/sales/retail/${create.data.id}/post`, {})
  check(step, { kind: 'api', description: 'Walk-in return /post → 204', ok: post.status === 204, detail: `${post.status} ${asString(post.data).slice(0, 200)}` })
  const after = await getStock(a.token, a.storeId, a.productId)
  check(step, { kind: 'api', description: 'Stock += 3 (товар вернулся даже без чека)', ok: after - before === 3, detail: `${before} → ${after}` })
}

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

export async function cr06_multi_tenant_isolation({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA || !ctx.orgB || !ctx.returnIdA) { step.status = 'skip'; return }
  const apiB = makeClient(ctx.orgB.token)
  const get = await apiB.get(`/api/sales/retail/${ctx.returnIdA}`)
  check(step, { kind: 'api', description: 'GET чужого id → 404', ok: get.status === 404, detail: `${get.status}` })
  const cr = await apiB.post(`/api/sales/retail/${ctx.saleIdA}/create-return`, {})
  check(step, { kind: 'api', description: 'POST /create-return на чужой → 404', ok: cr.status === 404, detail: `${cr.status}` })
}
