import { Hono } from 'hono'
import { beforeEach, describe, expect, it, vi } from 'vitest'

const { canMock, getDbMock } = vi.hoisted(() => ({
  canMock: vi.fn<(account: string, feature: string) => Promise<boolean>>(),
  getDbMock: vi.fn(() => ({ kind: 'db' })),
}))

vi.mock('@platform-modules/entitlements', () => ({
  createEntitlements: vi.fn(() => ({
    can: canMock,
  })),
}))

vi.mock('../db.js', () => ({
  getDb: getDbMock,
}))

import { ok, onError } from '../http.js'
import { requirePluginAccess } from './access.js'

type AccessVariables = {
  account: string
  capabilities: string[]
}

async function readJson(response: Response): Promise<unknown> {
  return response.json()
}

function createApp(input: { capabilities: string[] }) {
  const app = new Hono<{
    Bindings: {
      DATABASE_URL?: string
      HYPERDRIVE?: {
        connectionString?: string
      }
    }
    Variables: AccessVariables
  }>()

  app.onError(onError)
  app.use('*', async (context, next) => {
    context.set('account', 'account-1')
    context.set('capabilities', input.capabilities)
    await next()
  })
  app.use('*', requirePluginAccess('plugin:translate', 'translate.write'))
  app.get('/guarded', () => ok({ ok: true }))

  return app
}

describe('requirePluginAccess', () => {
  beforeEach(() => {
    canMock.mockReset()
    getDbMock.mockClear()
  })

  it('calls next when the account is entitled and the capability is present', async () => {
    canMock.mockResolvedValue(true)
    const app = createApp({ capabilities: ['translate.write'] })

    const response = await app.request('http://press-zone.test/guarded', undefined, {
      DATABASE_URL: 'postgres://postgres:postgres@127.0.0.1:5432/press_zone',
    })

    expect(response.status).toBe(200)
    await expect(readJson(response)).resolves.toEqual({ data: { ok: true } })
    expect(getDbMock).toHaveBeenCalledTimes(1)
    expect(canMock).toHaveBeenCalledWith('account-1', 'plugin:translate')
  })

  it('returns 402 PLUGIN_NOT_ENTITLED when the account lacks the entitlement', async () => {
    canMock.mockResolvedValue(false)
    const app = createApp({ capabilities: ['translate.write'] })

    const response = await app.request('http://press-zone.test/guarded', undefined, {
      DATABASE_URL: 'postgres://postgres:postgres@127.0.0.1:5432/press_zone',
    })

    expect(response.status).toBe(402)
    await expect(readJson(response)).resolves.toEqual({
      error: {
        code: 'PLUGIN_NOT_ENTITLED',
        message: 'Plugin is not entitled',
      },
    })
  })

  it('returns 403 FORBIDDEN when the account is entitled but the capability is absent', async () => {
    canMock.mockResolvedValue(true)
    const app = createApp({ capabilities: [] })

    const response = await app.request('http://press-zone.test/guarded', undefined, {
      DATABASE_URL: 'postgres://postgres:postgres@127.0.0.1:5432/press_zone',
    })

    expect(response.status).toBe(403)
    await expect(readJson(response)).resolves.toEqual({
      error: {
        code: 'FORBIDDEN',
        message: 'Forbidden',
      },
    })
  })
})
