/**
 * Sprint UI-deep, пункт 13: Multi-tenant изоляция через URL.
 * Создаём 2 организации A и B. A создаёт товар.
 * B пытается:
 *  - Открыть UI /catalog/products/{id-A} → 404 (не 200 с чужими данными)
 *  - Прямой API GET → 404
 *  - PUT/DELETE → 403/404, не успех
 *
 * Это P0 проверка — любая утечка = critical bug.
 */
import { test, expect, request as apiRequest } from '@playwright/test'
import { apiSignup, attachSession, watchPage } from '../lib/ui.js'

const BASE = process.env.E2E_ADMIN_URL ?? 'https://test.admin.food-market.kz'

async function seedProduct(token: string) {
  const ctx = await apiRequest.newContext({
    baseURL: BASE, ignoreHTTPSErrors: true,
    extraHTTPHeaders: { Authorization: `Bearer ${token}` },
  })
  type Paged<T> = { items: T[] }
  const units = await (await ctx.get('/api/catalog/units-of-measure?pageSize=200')).json() as Paged<{ id: string; code: string }>
  const groups = await (await ctx.get('/api/catalog/product-groups')).json() as Paged<{ id: string }>
  const pts = await (await ctx.get('/api/catalog/price-types')).json() as Paged<{ id: string; isRetail: boolean }>
  const curs = await (await ctx.get('/api/catalog/currencies')).json() as Paged<{ id: string; code: string }>

  const r = await ctx.post('/api/catalog/products', {
    data: {
      name: `IsolationTest ${Date.now()}`,
      article: `ISO-${Date.now()}`,
      unitOfMeasureId: units.items.find(u => u.code === '796')!.id,
      vat: 12, vatEnabled: true,
      productGroupId: groups.items[0].id, packaging: 1,
      prices: [{ priceTypeId: pts.items.find(p => p.isRetail)!.id, amount: 999, currencyId: curs.items.find(c => c.code === 'KZT')!.id }],
      barcodes: [{ code: `1100000${Date.now().toString().slice(-6)}`.slice(0, 13), type: 1, isPrimary: true }],
    },
  })
  expect([200, 201]).toContain(r.status())
  const p = await r.json() as { id: string; name: string }
  await ctx.dispose()
  return p
}

test.describe('UI-13 multi-tenant URL isolation (P0)', () => {
  test.describe.configure({ mode: 'serial' })

  test('13.1 B не видит товар A по прямому API GET', async () => {
    const orgA = await apiSignup('isoA1')
    const orgB = await apiSignup('isoB1')
    const productA = await seedProduct(orgA.accessToken)

    const ctxB = await apiRequest.newContext({
      baseURL: BASE, ignoreHTTPSErrors: true,
      extraHTTPHeaders: { Authorization: `Bearer ${orgB.accessToken}` },
    })
    const r = await ctxB.get(`/api/catalog/products/${productA.id}`, { failOnStatusCode: false })
    // 404 (правильно: не «видит чужое» а нет такого) или 403. НИКОГДА 200.
    expect(
      r.status(),
      `B пытается GET товар A id=${productA.id} → должно быть 404/403, НЕ 200`,
    ).not.toBe(200)
    expect([403, 404]).toContain(r.status())
    await ctxB.dispose()
  })

  test('13.2 B пытается PUT товар A → 404/403', async () => {
    const orgA = await apiSignup('isoA2')
    const orgB = await apiSignup('isoB2')
    const productA = await seedProduct(orgA.accessToken)

    const ctxB = await apiRequest.newContext({
      baseURL: BASE, ignoreHTTPSErrors: true,
      extraHTTPHeaders: { Authorization: `Bearer ${orgB.accessToken}` },
    })
    const r = await ctxB.put(`/api/catalog/products/${productA.id}`, {
      data: { name: 'HACKED', article: 'HACK', unitOfMeasureId: '00000000-0000-0000-0000-000000000000', vat: 0, vatEnabled: false, productGroupId: '', packaging: 1, prices: [], barcodes: [] },
      failOnStatusCode: false,
    })
    expect(r.status(), 'B пытается PUT товар A').not.toBe(200)
    expect([400, 403, 404]).toContain(r.status())
    // Дополнительно проверим что товар A не изменился
    const ctxA = await apiRequest.newContext({
      baseURL: BASE, ignoreHTTPSErrors: true,
      extraHTTPHeaders: { Authorization: `Bearer ${orgA.accessToken}` },
    })
    const stillThere = await ctxA.get(`/api/catalog/products/${productA.id}`)
    expect(stillThere.status()).toBe(200)
    const body = await stillThere.json() as { name: string }
    expect(body.name).not.toBe('HACKED')
    await ctxA.dispose()
    await ctxB.dispose()
  })

  test('13.3 B пытается DELETE товар A → 404/403', async () => {
    const orgA = await apiSignup('isoA3')
    const orgB = await apiSignup('isoB3')
    const productA = await seedProduct(orgA.accessToken)

    const ctxB = await apiRequest.newContext({
      baseURL: BASE, ignoreHTTPSErrors: true,
      extraHTTPHeaders: { Authorization: `Bearer ${orgB.accessToken}` },
    })
    const r = await ctxB.delete(`/api/catalog/products/${productA.id}`, { failOnStatusCode: false })
    expect([403, 404]).toContain(r.status())

    // A товар всё ещё видит
    const ctxA = await apiRequest.newContext({
      baseURL: BASE, ignoreHTTPSErrors: true,
      extraHTTPHeaders: { Authorization: `Bearer ${orgA.accessToken}` },
    })
    const stillThere = await ctxA.get(`/api/catalog/products/${productA.id}`)
    expect(stillThere.status()).toBe(200)
    await ctxA.dispose()
    await ctxB.dispose()
  })

  test('13.4 B открывает UI /catalog/products/{id-A} → видит 404, нет утечки', async ({ page }) => {
    test.setTimeout(60_000)
    const orgA = await apiSignup('isoA4')
    const orgB = await apiSignup('isoB4')
    const productA = await seedProduct(orgA.accessToken)

    const errs = watchPage(page, {
      // 404 на /api/catalog/products/{id} ожидаем
      expected4xxContains: [`/api/catalog/products/${productA.id}`],
    })
    await attachSession(page, orgB, `/catalog/products/${productA.id}`)
    await page.waitForLoadState('networkidle')

    // UI должен:
    // - НЕ показать имя товара A
    // - Показать пустую форму или toast «Не найдено»
    // - НЕ позволить редактировать
    const bodyText = await page.locator('body').innerText()
    expect(bodyText, 'B видит имя товара A — УТЕЧКА').not.toContain(productA.name)
    // toast «Не найдено» допустим
  })

  test('13.5 B в списке /catalog/products видит только СВОИ товары', async ({ page }) => {
    const orgA = await apiSignup('isoA5')
    const orgB = await apiSignup('isoB5')
    const productA = await seedProduct(orgA.accessToken)

    await attachSession(page, orgB, '/catalog/products')
    await page.waitForLoadState('networkidle')

    // У B нет товаров — должна быть EmptyState
    const bodyText = await page.locator('body').innerText()
    expect(bodyText, 'B видит товар A в списке — УТЕЧКА').not.toContain(productA.name)
    // EmptyState текст
    expect(bodyText).toMatch(/здесь пока пусто|товары появятся|создать первый/i)
  })
})
