import { beforeEach, describe, expect, it, vi } from 'vitest'
import { Hono } from 'hono'
import type { AppEnv } from '../src/types'

const mockGetWhiteLabelConfig = vi.fn()
const mockUpsertWhiteLabelConfig = vi.fn()
const mockInitiateWhiteLabelDomainVerification = vi.fn()
const mockListTenantApiKeys = vi.fn()
const mockCreateTenantApiKey = vi.fn()
const mockRevokeTenantApiKey = vi.fn()

vi.mock('@zync/db/queries', () => ({
  getWhiteLabelConfig: (...args: unknown[]) => mockGetWhiteLabelConfig(...args),
  upsertWhiteLabelConfig: (...args: unknown[]) => mockUpsertWhiteLabelConfig(...args),
  initiateWhiteLabelDomainVerification: (...args: unknown[]) =>
    mockInitiateWhiteLabelDomainVerification(...args),
  listTenantApiKeys: (...args: unknown[]) => mockListTenantApiKeys(...args),
  createTenantApiKey: (...args: unknown[]) => mockCreateTenantApiKey(...args),
  revokeTenantApiKey: (...args: unknown[]) => mockRevokeTenantApiKey(...args),
  createDb: vi.fn(() => ({ kind: 'db' })),
}))

vi.mock('../src/middleware/auth', () => ({
  authMiddleware: async (c: { set: (key: string, value: unknown) => void }, next: () => Promise<void>) => {
    c.set('session', {
      type: 'user',
      tid: 'tenant-1',
      sub: 'user-1',
      role: 'owner',
    })
    await next()
  },
}))

vi.mock('../src/middleware/guards', () => ({
  requirePermission: () => async (_c: unknown, next: () => Promise<void>) => {
    await next()
  },
  requireTier: () => async (_c: unknown, next: () => Promise<void>) => {
    await next()
  },
}))

import { whiteLabelSettingsRoute } from '../src/routes/settings/white-label'
import { apiKeysRoutes } from '../src/routes/api-keys'

function makeBindings() {
  return {
    KV: {
      get: vi.fn().mockResolvedValue(null),
      put: vi.fn().mockResolvedValue(undefined),
    },
    INTEGRATION_ENCRYPTION_KEY: btoa('12345678901234567890123456789012'),
  } as unknown as AppEnv['Bindings']
}

describe('white-label custom domain route', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockUpsertWhiteLabelConfig.mockResolvedValue({
      id: 'cfg-1',
      customDomain: 'portal.acme.com',
      portalDomain: 'portal.acme.com',
      sslStatus: 'pending',
    })
    mockGetWhiteLabelConfig.mockResolvedValue({
      id: 'cfg-1',
      customDomain: 'portal.acme.com',
      portalDomain: 'portal.acme.com',
      sslStatus: 'pending',
    })
    mockInitiateWhiteLabelDomainVerification.mockResolvedValue({
      cnameTarget: 'portal.zync.is',
    })
  })

  it('stores the custom domain in the authoritative customDomain field', async () => {
    const app = new Hono<AppEnv>()
    app.route('/', whiteLabelSettingsRoute)

    const res = await app.request(
      '/',
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ domain: 'portal.acme.com' }),
      },
      makeBindings(),
    )

    expect(res.status).toBe(200)
    expect(mockUpsertWhiteLabelConfig).toHaveBeenCalledWith(
      undefined,
      'tenant-1',
      'user-1',
      expect.objectContaining({
        customDomain: 'portal.acme.com',
      }),
    )
  })

  it('returns the DNS and verification fields the UI needs', async () => {
    mockGetWhiteLabelConfig.mockResolvedValue({
      id: 'cfg-1',
      customDomain: 'portal.acme.com',
      portalDomain: 'portal.acme.com',
      sslStatus: 'failed',
      verifiedAt: '2026-07-01T00:00:00.000Z',
      sslVerificationError: 'CNAME not found: portal.acme.com',
    })

    const app = new Hono<AppEnv>()
    app.route('/', whiteLabelSettingsRoute)

    const res = await app.request('/', undefined, makeBindings())

    expect(res.status).toBe(200)
    await expect(res.json()).resolves.toEqual({
      domain: 'portal.acme.com',
      status: 'failed',
      cname_target: 'portal.zync.is',
      error_message: 'CNAME not found: portal.acme.com',
      verified_at: '2026-07-01T00:00:00.000Z',
    })
  })
})

describe('api key management routes', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockListTenantApiKeys.mockResolvedValue([
      {
        id: 'key-1',
        tenant_id: 'tenant-1',
        name: 'Zapier',
        key_prefix: 'zyk_live',
        scopes: ['customers:read'],
        created_at: '2026-07-02T00:00:00.000Z',
        last_used_at: null,
        revoked_at: null,
        created_by: 'user-1',
      },
    ])
    mockCreateTenantApiKey.mockResolvedValue({
      id: 'key-1',
      tenant_id: 'tenant-1',
      name: 'Zapier',
      key_prefix: 'zyk_live',
      scopes: ['customers:read'],
      created_at: '2026-07-02T00:00:00.000Z',
      last_used_at: null,
      revoked_at: null,
      created_by: 'user-1',
    })
    mockRevokeTenantApiKey.mockResolvedValue(true)
  })

  it('creates a one-time plaintext key in the white-label spec format', async () => {
    const app = new Hono<AppEnv>()
    app.route('/', apiKeysRoutes)

    const res = await app.request(
      '/',
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name: 'Zapier', scopes: ['customers:read'] }),
      },
      makeBindings(),
    )

    expect(res.status).toBe(201)
    await expect(res.json()).resolves.toMatchObject({
      id: 'key-1',
      name: 'Zapier',
      scopes: ['customers:read'],
      key: expect.stringMatching(/^zyk_live_[a-z0-9]{32}$/),
    })
  })

  it('lists tenant api keys without returning the full key', async () => {
    const app = new Hono<AppEnv>()
    app.route('/', apiKeysRoutes)

    const res = await app.request('/', undefined, makeBindings())

    expect(res.status).toBe(200)
    await expect(res.json()).resolves.toEqual([
      expect.objectContaining({
        id: 'key-1',
        keyPrefix: 'zyk_live',
      }),
    ])
  })
})
