import { eq, sql } from 'drizzle-orm'
import { describe, expect, it } from 'vitest'

import { createPgliteClient } from '../../../../packages/db/src/postgres/pglite.ts'
import { createApp } from '../../src/index.js'
import { createOauthRoute } from '../../src/routes/oauth.js'
import {
  accounts,
  credentials,
  oauthClients,
  oauthTokens,
  plugins,
  pressZoneInitSql,
  pressZoneSchema,
  sites,
} from '../../src/schema.js'

type TestDb = ReturnType<typeof createPgliteClient<typeof pressZoneSchema>>

async function createTestDb(): Promise<TestDb> {
  const db = createPgliteClient({ schema: pressZoneSchema })

  for (const statement of pressZoneInitSql.split(';').map((part) => part.trim()).filter(Boolean)) {
    await db.execute(sql.raw(statement))
  }

  return db
}

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

async function seedAccountAndPlugin(db: TestDb): Promise<{ accountId: string }> {
  const accountId = '11111111-1111-1111-1111-111111111111'

  await db.insert(accounts).values({
    id: accountId,
    slug: 'acct-press-zone',
  })
  await db.insert(plugins).values({
    key: 'translate',
    name: 'Translate',
  })

  return { accountId }
}

async function deriveCodeChallengeS256(codeVerifier: string): Promise<string> {
  const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier))
  const bytes = new Uint8Array(digest)
  let binary = ''

  for (const byte of bytes) {
    binary += String.fromCharCode(byte)
  }

  return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '')
}

describe('HTTP integration: connect flow', () => {
  it('authorizes and exchanges a token through the mounted oauth app path', async () => {
    const db = await createTestDb()
    const { accountId } = await seedAccountAndPlugin(db)
    const client = {
      id: 'translate',
      name: 'Translate',
      redirectUris: ['https://plugin.example.com/oauth/callback'],
      scopes: ['plugin:translate'],
      confidential: false,
    }
    await db.insert(oauthClients).values({
      id: client.id,
      name: client.name,
      redirectUris: JSON.stringify(client.redirectUris),
      scopes: JSON.stringify(client.scopes),
      confidential: false,
      clientSecretHash: null,
      createdAt: new Date('2026-07-04T00:00:00.000Z'),
    })
    const verifier = 'pkce-verifier-http-integration'
    const challenge = await deriveCodeChallengeS256(verifier)
    const app = createApp({
      oauthRoute: createOauthRoute({ db }),
    })

    const authorizeResponse = await app.request(
      `http://press-zone.test/oauth/authorize?client_id=${client.id}&redirect_uri=${encodeURIComponent(
        client.redirectUris[0]!,
      )}&scope=plugin%3Atranslate&state=connect-state&code_challenge=${challenge}&code_challenge_method=S256`,
    )

    expect(authorizeResponse.status).toBe(200)
    const authorizePayload = (await readJson(authorizeResponse)) as {
      data: {
        code: string
        state: string
      }
    }
    expect(authorizePayload).toEqual({
      data: {
        code: expect.any(String),
        state: 'connect-state',
      },
    })

    const tokenResponse = await app.request('http://press-zone.test/oauth/token', {
      method: 'POST',
      headers: {
        'content-type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        grant_type: 'authorization_code',
        code: authorizePayload.data.code,
        code_verifier: verifier,
        client_id: client.id,
        account_id: accountId,
      }).toString(),
    })

    expect(tokenResponse.status).toBe(200)
    await expect(readJson(tokenResponse)).resolves.toEqual({
      data: {
        accessToken: expect.any(String),
        tokenType: 'Bearer',
        expiresIn: 3600,
        scope: 'plugin:translate',
      },
    })

    const [site] = await db.select().from(sites).limit(1)
    expect(site).toMatchObject({
      accountId,
      pluginKey: 'translate',
      displayUrl: 'https://plugin.example.com/oauth/callback',
      status: 'active',
    })

    const [credential] = await db.select().from(credentials).limit(1)
    expect(credential).toMatchObject({
      accountId,
      siteId: site?.id,
      provider: 'wordpress',
      apiKeyId: null,
    })

    const [token] = await db
      .select({
        id: oauthTokens.id,
        clientId: oauthTokens.clientId,
        tokenType: oauthTokens.tokenType,
        scope: oauthTokens.scope,
      })
      .from(oauthTokens)
      .where(eq(oauthTokens.clientId, client.id))
      .limit(1)

    expect(token).toMatchObject({
      clientId: 'translate',
      tokenType: 'Bearer',
      scope: 'plugin:translate',
    })
  })
})
