import { sql } from 'drizzle-orm'
import { describe, expect, it } from 'vitest'
import { createPgliteClient } from '@platform-modules/db/pglite'
import { EngineMismatchError, getSession } from './index.js'
import { createBetterAuthEngine, betterAuthSchema } from './engine-better-auth.js'

const CREATE_PLATFORM_ACCOUNTS = sql`
  CREATE TABLE platform_accounts (
    id text PRIMARY KEY,
    email varchar(255) NOT NULL UNIQUE,
    name varchar(255),
    "emailVerified" boolean DEFAULT false,
    avatar_url text,
    created_at timestamptz,
    updated_at timestamptz
  )
`

const CREATE_BA_SESSION = sql`
  CREATE TABLE session (
    id text PRIMARY KEY,
    token text NOT NULL UNIQUE,
    "userId" text NOT NULL,
    "expiresAt" timestamptz NOT NULL,
    "createdAt" timestamptz NOT NULL,
    "updatedAt" timestamptz NOT NULL,
    "ipAddress" text,
    "userAgent" text
  )
`

const CREATE_BA_ACCOUNT = sql`
  CREATE TABLE account (
    id text PRIMARY KEY,
    "userId" text NOT NULL,
    issuer text NOT NULL,
    "accountId" text NOT NULL,
    "providerId" text NOT NULL,
    "accessToken" text,
    "refreshToken" text,
    "idToken" text,
    "expiresAt" timestamptz,
    password text,
    "createdAt" timestamptz NOT NULL,
    "updatedAt" timestamptz NOT NULL,
    "accessTokenExpiresAt" timestamptz,
    "refreshTokenExpiresAt" timestamptz,
    scope text,
    UNIQUE (issuer, "accountId")
  )
`

const CREATE_BA_VERIFICATION = sql`
  CREATE TABLE verification (
    id text PRIMARY KEY,
    identifier text NOT NULL,
    value text NOT NULL,
    "expiresAt" timestamptz NOT NULL,
    "createdAt" timestamptz,
    "updatedAt" timestamptz
  )
`

const CREATE_BA_JWKS = sql`
  CREATE TABLE jwks (
    id text PRIMARY KEY,
    "publicKey" text NOT NULL,
    "privateKey" text NOT NULL,
    "createdAt" timestamptz NOT NULL,
    "expiresAt" timestamptz,
    alg text,
    crv text
  )
`

async function setupBetterAuth() {
  const db = createPgliteClient({ schema: betterAuthSchema })
  await db.execute(CREATE_PLATFORM_ACCOUNTS)
  await db.execute(CREATE_BA_SESSION)
  await db.execute(CREATE_BA_ACCOUNT)
  await db.execute(CREATE_BA_VERIFICATION)
  await db.execute(CREATE_BA_JWKS)
  const engine = createBetterAuthEngine({ db, secret: 'better-auth-test-secret' })
  return { db, engine }
}

describe('createBetterAuthEngine', () => {
  it('throws EngineMismatchError for verifyPassword and setPassword; refresh uses sessionCookieName', async () => {
    const { engine } = await setupBetterAuth()
    await expect(engine.verifyPassword('pass', 'hash')).rejects.toBeInstanceOf(EngineMismatchError)
    await expect(engine.verifyPassword('pass', 'hash')).rejects.toThrow(
      'verifyPassword not supported by engine-better-auth — route credentials through the better-auth sign-in handler',
    )
    await expect(engine.setPassword('user-id', 'new-pass')).rejects.toBeInstanceOf(EngineMismatchError)
    await expect(engine.setPassword('user-id', 'new-pass')).rejects.toThrow(
      'setPassword not supported by engine-better-auth — use the better-auth change-password handler',
    )

    const db = createPgliteClient({ schema: betterAuthSchema })
    await db.execute(CREATE_PLATFORM_ACCOUNTS)
    await db.execute(CREATE_BA_SESSION)
    await db.execute(CREATE_BA_ACCOUNT)
    await db.execute(CREATE_BA_VERIFICATION)
    await db.execute(CREATE_BA_JWKS)
    const prefixedEngine = createBetterAuthEngine({
      db,
      secret: 'better-auth-test-secret',
      cookiePrefix: 'myapp',
    })
    await prefixedEngine.createUser({ email: 'prefix@example.com', password: 'Password1!' })
    const signedIn = await prefixedEngine.signIn({ email: 'prefix@example.com', password: 'Password1!' })
    await expect(prefixedEngine.refresh(signedIn.accessToken)).resolves.toMatchObject({
      principal: { userId: expect.any(String) },
      accessToken: signedIn.accessToken,
    })
  })

  it('satisfies sign-in → verifySession → getSession (not wire-compatible with engine-custom)', async () => {
    const { engine } = await setupBetterAuth()
    await engine.createUser({ email: 'ba@example.com', password: 'Password1!' })
    const signedIn = await engine.signIn({ email: 'ba@example.com', password: 'Password1!' })
    const principal = await engine.verifySession(signedIn.accessToken)
    expect(principal?.userId).toBeTruthy()
    await expect(
      getSession(
        new Headers({ cookie: `platform.session_token=${signedIn.accessToken}` }),
        engine,
        { accessCookieName: 'platform.session_token' },
      ),
    ).resolves.toMatchObject({ userId: principal!.userId })
  })

  it('signOut actually revokes the session; createUser rejects roles instead of silently dropping them', async () => {
    const { engine } = await setupBetterAuth()
    await engine.createUser({ email: 'so@example.com', password: 'Password1!' })
    const signIn = await engine.signIn({ email: 'so@example.com', password: 'Password1!' })
    await expect(engine.verifySession(signIn.accessToken)).resolves.not.toBeNull()

    await engine.signOut(signIn.principal.sessionId)
    await expect(engine.verifySession(signIn.accessToken)).resolves.toBeNull()

    await expect(
      engine.createUser({ email: 'roles@example.com', password: 'Password1!', roles: ['admin'] }),
    ).rejects.toBeInstanceOf(EngineMismatchError)
  })
})
