/**
 * Прямой доступ к Postgres для preconditions (TRUNCATE) и data-checks
 * (SELECT). Работает через docker exec на контейнер food-market-postgres,
 * так что не зависит от того, проброшен ли порт наружу.
 */
import { execFileSync } from 'node:child_process'

export function psql(sql: string): string {
  // Использую pgcli/psql внутри контейнера. -tA = bare output без заголовков.
  return execFileSync(
    'docker',
    ['exec', '-i', 'food-market-postgres', 'psql', '-U', 'food_market', '-d', 'food_market', '-tAv', 'ON_ERROR_STOP=1', '-c', sql],
    { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], maxBuffer: 16 * 1024 * 1024 },
  )
}

export function psqlRows(sql: string): string[][] {
  const out = psql(sql).trim()
  if (!out) return []
  return out.split('\n').map((line) => line.split('|'))
}

/**
 * TRUNCATE для preconditions full-cycle сценария.
 *
 * Реестр товаров и системные справочники — не трогаем. Платформенные настройки,
 * миграции и OpenIddict — не трогаем. Identity SuperAdmin (admin@food-market.local)
 * сохраняем; всех остальных AspNetUsers/UserRoles/Employees/Organizations
 * чистим.
 */
export function resetTenantData(): void {
  // Список таблиц получаем из информационной схемы — устойчиво к новым доменным
  // сущностям. Исключаем «keep-list» по подстроке (lowercase). Ниже — порядок
  // вырезания: сначала зависимые таблицы, потом ссылочные.
  const KEEP = new Set([
    // Реестр товаров
    'products', 'product_groups', 'units_of_measure', 'product_packagings',
    'product_barcodes', 'product_prices', 'product_images',
    // Справочники
    'countries', 'currencies', 'price_types', 'employee_roles',
    // Инфраструктура
    '__EFMigrationsHistory', 'platform_settings', 'system_settings',
  ])
  const KEEP_PREFIXES = ['OpenIddict']

  const all = psqlRows(`SELECT tablename FROM pg_tables WHERE schemaname='public' ORDER BY tablename`)
    .map((r) => r[0])
    .filter(Boolean)

  // Identity-табицы (AspNet*, users, roles) обрабатываем отдельно с фильтром
  // SuperAdmin — нельзя просто TRUNCATE.
  const IDENTITY = ['AspNetUserClaims', 'AspNetUserLogins', 'AspNetUserTokens', 'AspNetUserRoles', 'AspNetRoleClaims', 'users', 'roles']
  const isIdentity = (t: string) => IDENTITY.includes(t)
  const tenantTables = all
    .filter((t) => !KEEP.has(t))
    .filter((t) => !KEEP_PREFIXES.some((p) => t.startsWith(p)))
    .filter((t) => !isIdentity(t))

  if (tenantTables.length > 0) {
    // Bulk TRUNCATE с CASCADE — порядок не важен.
    const list = tenantTables.map((t) => `"${t}"`).join(', ')
    psql(`TRUNCATE TABLE ${list} RESTART IDENTITY CASCADE;`)
  }

  // Identity-зачистка: оставляем только SuperAdmin'а.
  psql(`
    DELETE FROM "AspNetUserRoles" ur
    USING users u
    WHERE ur."UserId" = u."Id"
      AND lower(u."Email") <> 'admin@food-market.local';

    DELETE FROM "AspNetUserClaims" uc
    USING users u
    WHERE uc."UserId" = u."Id"
      AND lower(u."Email") <> 'admin@food-market.local';

    DELETE FROM "AspNetUserLogins" ul
    USING users u
    WHERE ul."UserId" = u."Id"
      AND lower(u."Email") <> 'admin@food-market.local';

    DELETE FROM "AspNetUserTokens" ut
    USING users u
    WHERE ut."UserId" = u."Id"
      AND lower(u."Email") <> 'admin@food-market.local';

    DELETE FROM users WHERE lower("Email") <> 'admin@food-market.local';

    -- SuperAdmin отвязываем от любой удалённой org.
    UPDATE users SET "OrganizationId" = NULL, "IsActive" = true
     WHERE lower("Email") = 'admin@food-market.local';

    -- OpenIddict tokens деактивируем (refresh-tokens чужих юзеров).
    UPDATE "OpenIddictTokens" SET "Status" = 'revoked' WHERE "Status" = 'valid';
  `)
}

export function countRows(table: string, where = ''): number {
  const sql = `SELECT count(*) FROM "${table}" ${where ? 'WHERE ' + where : ''};`
  const out = psql(sql).trim()
  return Number(out) || 0
}
