/**
 * Stage OrgAuditLog: проверка журнала мутаций через CRUD продукта.
 */
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
  currencyId: string; unitKgId: string; groupId: string; priceTypeRetailId: string
}
type Ctx = {
  apiOnly: boolean
  ts: number
  orgA?: Org
  orgB?: Org
  productIdA?: 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): Promise<Org> {
  const api = makeClient()
  const email = `stage-au-${suffix}-${TS}@food-market.local`
  const password = 'StageAu12345!'
  let r = await api.post('/api/auth/signup', { email, password, organizationName: `Au ${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: `Au ${suffix} ${TS}`, phone, plan: 'start' })
  }
  if (r.status !== 200) throw new Error(`signup ${suffix}: ${r.status}`)
  const sess = await login(email, password)
  const auth = makeClient(sess.accessToken)
  const [stores, units, groups, prices, currencies] = 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'),
  ])
  return {
    orgId: r.data.organizationId, email, password, token: sess.accessToken,
    storeId: stores.data.items.find((s: { isMain: boolean }) => s.isMain).id,
    unitKgId: units.data.items.find((u: { code: string }) => u.code === '166').id,
    groupId: groups.data.items.find((g: { parentId: string | null }) => g.parentId == null).id,
    priceTypeRetailId: prices.data.items.find((p: { isRequired: boolean }) => p.isRequired).id,
    currencyId: currencies.data.items.find((c: { code: string }) => c.code === 'KZT').id,
  }
}

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

export async function au01_setup({ ctx, step, report }: StepCtx) {
  ctx.ts = TS
  ctx.orgA = await bootstrapOrg('a', '+77011190001')
  await new Promise(r => setTimeout(r, 1500))
  ctx.orgB = await bootstrapOrg('b', '+77011190002')
  // Audit log endpoint должен ответить 200 на пустой
  const r = await makeClient(ctx.orgA.token).get('/api/admin/audit-log?pageSize=10')
  check(step, {
    kind: 'api', description: 'GET /api/admin/audit-log → 200 для нового tenant',
    ok: r.status === 200,
    detail: `${r.status} total=${r.data?.total}`,
  })
}

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

export async function au02_create_product_logged({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const prod = await api.post('/api/catalog/products', {
    name: `Audit Prod ${TS}`, article: `AU-${TS}`,
    unitOfMeasureId: a.unitKgId, vat: 0, vatEnabled: false, productGroupId: a.groupId,
    barcodes: [{ code: generateEan13(91), type: 1, isPrimary: true }],
    prices: [{ priceTypeId: a.priceTypeRetailId, amount: 100, currencyId: a.currencyId }],
  })
  check(step, { kind: 'api', description: 'POST product → 201', ok: prod.status === 201, detail: `${prod.status}` })
  if (prod.status !== 201) return
  ctx.productIdA = prod.data.id

  await new Promise(r => setTimeout(r, 300))
  const audit = await api.get(`/api/admin/audit-log?entityType=Product&entityId=${ctx.productIdA}&pageSize=10`)
  check(step, { kind: 'api', description: 'GET audit-log по новому product', ok: audit.status === 200, detail: `${audit.status} total=${audit.data?.total}` })
  const create = (audit.data?.items ?? []).find((x: { action: string }) => x.action === 'create')
  check(step, {
    kind: 'api', description: 'Есть запись action=create с EntityType=Product',
    ok: !!create,
    detail: create ? `action=${create.action} type=${create.entityType}` : 'не найдена',
  })
  if (create) {
    let diff: Record<string, unknown> = {}
    try { diff = JSON.parse(create.changesJson) } catch { /* ignore */ }
    check(step, {
      kind: 'api', description: 'Diff содержит хотя бы поле Name',
      ok: 'Name' in diff,
      detail: `keys=${Object.keys(diff).slice(0, 5).join(',')}`,
    })
  }
}

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

export async function au03_update_product_logged({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA || !ctx.productIdA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const upd = await api.put(`/api/catalog/products/${ctx.productIdA}`, {
    name: `Audit Prod UPDATED ${TS}`, article: `AU-${TS}`,
    unitOfMeasureId: a.unitKgId, vat: 0, vatEnabled: false, productGroupId: a.groupId,
    barcodes: [{ code: generateEan13(91), type: 1, isPrimary: true }],
    prices: [{ priceTypeId: a.priceTypeRetailId, amount: 200, currencyId: a.currencyId }],
  })
  check(step, { kind: 'api', description: 'PUT product → 204', ok: upd.status === 204, detail: `${upd.status}` })

  await new Promise(r => setTimeout(r, 300))
  const audit = await api.get(`/api/admin/audit-log?entityType=Product&entityId=${ctx.productIdA}&action=update&pageSize=10`)
  const updRec = (audit.data?.items ?? []).find((x: { action: string }) => x.action === 'update')
  check(step, { kind: 'api', description: 'Есть запись action=update', ok: !!updRec, detail: updRec ? `action=${updRec.action}` : 'не найдена' })
  if (updRec) {
    let diff: Record<string, { before?: unknown; after?: unknown }> = {}
    try { diff = JSON.parse(updRec.changesJson) } catch { /* ignore */ }
    const name = diff['Name']
    check(step, {
      kind: 'api', description: 'Diff содержит Name с before и after',
      ok: name && 'before' in name && 'after' in name &&
        String(name.before).includes('Audit Prod') && String(name.after).includes('UPDATED'),
      detail: `Name before=${name?.before} after=${name?.after}`,
    })
  }
}

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

export async function au04_delete_product_logged({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA || !ctx.productIdA) { step.status = 'skip'; return }
  const a = ctx.orgA
  const api = makeClient(a.token)
  const del = await api.delete(`/api/catalog/products/${ctx.productIdA}`)
  check(step, { kind: 'api', description: 'DELETE product → 204', ok: del.status === 204, detail: `${del.status}` })

  await new Promise(r => setTimeout(r, 300))
  const audit = await api.get(`/api/admin/audit-log?entityType=Product&entityId=${ctx.productIdA}&action=delete&pageSize=10`)
  const delRec = (audit.data?.items ?? []).find((x: { action: string }) => x.action === 'delete')
  check(step, { kind: 'api', description: 'Есть запись action=delete', ok: !!delRec, detail: delRec ? `action=${delRec.action}` : 'не найдена' })
}

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

export async function au05_filter_by_entity_type({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA) { step.status = 'skip'; return }
  const api = makeClient(ctx.orgA.token)
  const r = await api.get('/api/admin/audit-log?entityType=Product&pageSize=50')
  const allProduct = (r.data?.items ?? []).every((x: { entityType: string }) => x.entityType === 'Product')
  check(step, {
    kind: 'api', description: 'Все записи — Product',
    ok: r.status === 200 && allProduct && (r.data?.items?.length ?? 0) > 0,
    detail: `${r.status} count=${r.data?.items?.length} allProduct=${allProduct}`,
  })
}

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

export async function au06_filter_by_action({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA) { step.status = 'skip'; return }
  const api = makeClient(ctx.orgA.token)
  const r = await api.get('/api/admin/audit-log?action=create&pageSize=50')
  const allCreate = (r.data?.items ?? []).every((x: { action: string }) => x.action === 'create')
  check(step, {
    kind: 'api', description: 'Все записи — action=create',
    ok: r.status === 200 && allCreate,
    detail: `${r.status} count=${r.data?.items?.length} allCreate=${allCreate}`,
  })
}

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

export async function au07_multi_tenant_isolation({ ctx, step, report }: StepCtx) {
  if (!ctx.orgA || !ctx.orgB) { step.status = 'skip'; return }
  const apiB = makeClient(ctx.orgB.token)
  const r = await apiB.get('/api/admin/audit-log?pageSize=50')
  const items = r.data?.items ?? []
  // Все записи должны принадлежать org B (никаких записей, у которых есть entityId
  // совпадающий с productIdA, тоже не должно быть)
  const sees = items.some((x: { entityId?: string }) => x.entityId === ctx.productIdA)
  check(step, {
    kind: 'api', description: 'Org B не видит записи о продукте Org A',
    ok: !sees,
    detail: `total=${r.data?.total} sees=${sees}`,
  })
  if (sees) {
    report.bug({
      step: 'au07', severity: 'critical',
      title: 'P0 multi-tenant утечка в /audit-log: Org B видит записи Org A',
      detail: `productId=${ctx.productIdA}`,
    })
  }
}
