import { describe, expect, it } from 'vitest'
import type { Querier } from '@platform-modules/db'
import { CatalogMigrateError, isCatalogMigrateError, pushSchema } from './migrate.js'
import { createPgliteClient } from '../../db/src/postgres/pglite.js'
import { catalogSchema } from './schema.js'

describe('commerce-catalog migrate', () => {
  it('pushSchema is idempotent (CREATE … IF NOT EXISTS) on a fresh and re-run DB', async () => {
    const db = createPgliteClient({ schema: catalogSchema })
    await pushSchema(db)
    // second push must not throw — IF NOT EXISTS guards the journal-free raw DDL
    await expect(pushSchema(db)).resolves.toBeUndefined()
  })

  it('wraps an underlying execute failure in CatalogMigrateError, carrying the root cause', async () => {
    const failing = {
      execute: async () => {
        throw new Error('boom: relation already exists differently')
      },
    } as unknown as Querier<typeof catalogSchema>

    await expect(pushSchema(failing)).rejects.toMatchObject({
      name: 'CatalogMigrateError',
      code: 'CATALOG_MIGRATE',
    })
    // the root cause must survive into detail (no ignored signals — operator diagnosability)
    await expect(pushSchema(failing)).rejects.toThrow(/boom: relation already exists differently/)
  })

  it('CatalogMigrateError carries detail and a stable name/code', () => {
    const e = new CatalogMigrateError('something failed')
    expect(e).toBeInstanceOf(Error)
    expect(e.name).toBe('CatalogMigrateError')
    expect(e.code).toBe('CATALOG_MIGRATE')
    expect(e.detail).toBe('something failed')
    expect(e.message).toContain('something failed')
  })

  it('non-Error throw values are stringified into detail', async () => {
    const failing = {
      execute: async () => {
        throw 'string failure' // eslint-disable-line no-throw-literal
      },
    } as unknown as Querier<typeof catalogSchema>

    await expect(pushSchema(failing)).rejects.toThrow(/string failure/)
  })

  it('isCatalogMigrateError narrows structurally (name+code), not by instanceof', () => {
    // true for a real instance
    expect(isCatalogMigrateError(new CatalogMigrateError('boom'))).toBe(true)
    // true for a cross-realm-shaped object — structural identity survives module duplication
    expect(isCatalogMigrateError({ name: 'CatalogMigrateError', code: 'CATALOG_MIGRATE' })).toBe(true)
    // false for a plain object / non-error value
    expect(isCatalogMigrateError({})).toBe(false)
    expect(isCatalogMigrateError(null)).toBe(false)
    expect(isCatalogMigrateError('CatalogMigrateError')).toBe(false)
    // false for a different error class
    expect(isCatalogMigrateError(new Error('catalog migrate: boom'))).toBe(false)
    // false for the right name but wrong code (and vice-versa)
    expect(isCatalogMigrateError({ name: 'CatalogMigrateError', code: 'WRONG' })).toBe(false)
    expect(isCatalogMigrateError({ name: 'OtherError', code: 'CATALOG_MIGRATE' })).toBe(false)
  })
})
