import { describe, expect, it } from 'vitest'
import {
  createMemoryTenancyStore,
  createTenancy,
  type IsolationAdapter,
  type RbacAdapter,
  type ScopedQuerier,
} from './index.js'

function stubIsolation(): IsolationAdapter & { calls: Array<{ tenantId: string }> } {
  const calls: Array<{ tenantId: string }> = []
  return {
    calls,
    scopeQuerier(q, tenantId) {
      calls.push({ tenantId })
      return q as unknown as ScopedQuerier
    },
  }
}

function stubRbac(caps: string[] = []): RbacAdapter {
  return {
    resolveCapabilities: async () => caps,
  }
}

describe('resolveCapabilities', () => {
  it('returns [] for unknown user without throwing', async () => {
    const tenancy = createTenancy({
      store: createMemoryTenancyStore(),
      isolation: stubIsolation(),
      rbac: stubRbac(['tasks:read']),
    })
    const tenant = await tenancy.createTenant({
      slug: 'acme',
      name: 'Acme',
      ownerId: 'owner-1',
    })
    await expect(tenancy.resolveCapabilities('ghost', tenant.id)).resolves.toEqual([])
  })

  it('returns [] for unknown tenant without throwing', async () => {
    const tenancy = createTenancy({
      store: createMemoryTenancyStore(),
      isolation: stubIsolation(),
      rbac: stubRbac(['tasks:read']),
    })
    await expect(tenancy.resolveCapabilities('user-1', 'missing-tenant')).resolves.toEqual([])
  })

  it('returns [] for frozen membership (revocation honoured)', async () => {
    const store = createMemoryTenancyStore()
    const tenancy = createTenancy({
      store,
      isolation: stubIsolation(),
      rbac: stubRbac(['tasks:read']),
    })
    const tenant = await tenancy.createTenant({
      slug: 'acme',
      name: 'Acme',
      ownerId: 'owner-1',
    })
    await store.addMember({
      tenantId: tenant.id,
      userId: 'user-1',
      roleKey: 'ADMIN',
      status: 'frozen',
    })
    await expect(tenancy.resolveCapabilities('user-1', tenant.id)).resolves.toEqual([])
  })

  it('returns [] for suspended tenant without throwing', async () => {
    const store = createMemoryTenancyStore()
    const tenancy = createTenancy({
      store,
      isolation: stubIsolation(),
      rbac: stubRbac(['tasks:read']),
    })
    const tenant = await tenancy.createTenant({
      slug: 'acme',
      name: 'Acme',
      ownerId: 'owner-1',
    })
    await store.addMember({
      tenantId: tenant.id,
      userId: 'user-1',
      roleKey: 'ADMIN',
      status: 'active',
    })
    await tenancy.suspendTenant(tenant.id)
    await expect(tenancy.resolveCapabilities('user-1', tenant.id)).resolves.toEqual([])
  })
})

describe('createTenancy', () => {
  it('wires scopeQuerier through the injected isolation adapter', () => {
    const isolation = stubIsolation()
    const tenancy = createTenancy({
      store: createMemoryTenancyStore(),
      isolation,
      rbac: stubRbac(),
    })
    const fakeQ = {} as never
    tenancy.scopeQuerier!(fakeQ, 'tenant-a')
    expect(isolation.calls).toEqual([{ tenantId: 'tenant-a' }])
  })

  it('round-trips tenant and membership CRUD', async () => {
    const tenancy = createTenancy({
      store: createMemoryTenancyStore(),
      isolation: stubIsolation(),
      rbac: stubRbac(),
    })
    const tenant = await tenancy.createTenant({
      slug: 'acme',
      name: 'Acme',
      ownerId: 'owner-1',
    })
    await tenancy.addMember({
      tenantId: tenant.id,
      userId: 'user-1',
      roleKey: 'MEMBER',
      status: 'active',
    })
    const members = await tenancy.listMembers(tenant.id)
    expect(members).toHaveLength(1)
    await tenancy.setMemberRole(tenant.id, 'user-1', 'ADMIN')
    const updated = await tenancy.listMembers(tenant.id)
    expect(updated[0]?.roleKey).toBe('ADMIN')
    await tenancy.removeMember(tenant.id, 'user-1')
    expect(await tenancy.listMembers(tenant.id)).toHaveLength(0)
  })
})
