import { describe, it, expect, beforeAll, vi } from 'vitest'
import { ApifyAdapter } from '../apify-adapter'
import type { ExecutionContext } from '../../../../../../base/types'
import { createClient } from '@supabase/supabase-js'
import type { Database } from '../../../../../../../../supabase/database.types'
import { decryptCredentialValue } from '../../../../../../../../auth/multi-tenancy/credentials/server/service'
import { loadCredentialKEKs } from '../../../../../../../../auth/multi-tenancy/credentials/server/kek-loader'
import type { RunActorResult } from '../fetch/run-actor'

/**
 * Apify Actor Integration Test Suite
 *
 * Tests the complete runActor flow:
 * 1. Start actor with input
 * 2. Poll for completion
 * 3. Fetch dataset results
 * 4. Handle various actor statuses
 *
 * Prerequisites:
 * - Supabase database with credentials table
 * - Credential 'elevasis-apify' with valid Apify API token
 * - Organization ID: f9aa5a56-8c13-4cd1-9161-8827ae7b452b
 * - SUPABASE_URL and SUPABASE_SERVICE_KEY env vars set
 * - Apify account accessible with provided credentials
 *
 * Run: pnpm test apify-run-actor.integration.test.ts
 */

const SKIP_TESTS = !process.env.SUPABASE_URL || !process.env.SUPABASE_SERVICE_KEY

describe.skipIf(SKIP_TESTS)('Apify Run Actor Integration Tests', () => {
  const adapter = new ApifyAdapter()
  const organizationId = 'f9aa5a56-8c13-4cd1-9161-8827ae7b452b'
  const credentialName = 'elevasis-apify'

  const context: ExecutionContext = {
    organizationId,
    executionId: 'apify-integration-test',
    resourceId: 'apify-test-agent',
    resourceType: 'agent',
    logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), child: vi.fn().mockReturnThis() }
  }

  let supabase: ReturnType<typeof createClient<Database>>
  let credentials: Record<string, unknown>

  beforeAll(async () => {
    console.log('\n=== Apify Actor Integration Test Suite ===')
    console.log(`Organization: ${organizationId}`)
    console.log(`Credential: ${credentialName}\n`)

    // Initialize Supabase
    supabase = createClient<Database>(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)

    // Load Vault KEK so platform-v1 ciphertext (post-Wave-B4 re-encrypted rows)
    // decrypts. Without this, the env-fallback would register the legacy key
    // under platform-v1 and AES-GCM auth-tag verification would fail.
    await loadCredentialKEKs()

    // Fetch credential from database
    const { data: credRow, error } = await supabase
      .from('credentials')
      .select('encrypted_value')
      .eq('organization_id', organizationId)
      .eq('name', credentialName)
      .single()

    if (error || !credRow) {
      throw new Error(`Credential '${credentialName}' not found for org ${organizationId}. Error: ${error?.message}`)
    }

    // Decrypt credentials
    credentials = decryptCredentialValue(credRow.encrypted_value)

    console.log('✓ Credentials loaded and decrypted')
    console.log('Credential fields:', Object.keys(credentials))
    console.log(
      'Credential values (masked):',
      Object.keys(credentials).reduce(
        (acc, key) => {
          acc[key] = typeof credentials[key] === 'string' ? credentials[key].substring(0, 10) + '...' : credentials[key]
          return acc
        },
        {} as Record<string, unknown>
      )
    )
    const tokenPresent = !!(credentials.apiToken || credentials.apiKey || credentials.api_token || credentials.token)
    console.log(`✓ API Token present: ${tokenPresent}`)

    // Validate credentials
    const isValid = adapter.validateCredentials(credentials)
    if (!isValid) {
      throw new Error('Credential validation failed')
    }
    console.log('✓ Credentials validated')
    console.log('\n✓ Ready to run tests')
  })

  it('should run a simple actor and return results', { timeout: 60000 }, async () => {
    console.log('\n[Test] Run simple hello-world actor')

    // Using apify/hello-world - a simple actor that runs quickly
    const result = (await adapter.call(
      'runActor',
      {
        actorId: 'apify/hello-world',
        input: {
          message: 'Integration test from Elevasis'
        },
        timeoutSecs: 60,
        pollIntervalSecs: 5
      },
      credentials,
      context
    )) as RunActorResult

    console.log(`✓ Actor completed with status: ${result.status}`)
    console.log(`✓ Run ID: ${result.runId}`)
    console.log(`✓ Dataset ID: ${result.datasetId}`)
    console.log(`✓ Items returned: ${result.totalCount}`)
    console.log(`✓ Execution time: ${result.executionTimeMs}ms`)

    expect(result).toBeDefined()
    expect(result.status).toBe('SUCCEEDED')
    expect(result.runId).toBeDefined()
    expect(result.datasetId).toBeDefined()
    expect(result.items).toBeInstanceOf(Array)
    expect(result.totalCount).toBeGreaterThanOrEqual(0)
    expect(result.executionTimeMs).toBeGreaterThan(0)
  })

  it('should handle actor with custom input', { timeout: 60000 }, async () => {
    console.log('\n[Test] Run actor with custom input')

    const result = (await adapter.call(
      'runActor',
      {
        actorId: 'apify/hello-world',
        input: {
          message: 'Custom test message',
          outputDatasetItems: 5
        },
        timeoutSecs: 60,
        pollIntervalSecs: 5
      },
      credentials,
      context
    )) as RunActorResult

    console.log(`✓ Actor completed: ${result.status}`)
    console.log(`✓ Items in dataset: ${result.totalCount}`)

    expect(result).toBeDefined()
    expect(result.status).toBe('SUCCEEDED')
    expect(result.items).toBeInstanceOf(Array)
  })

  it('should handle maxItems parameter', { timeout: 60000 }, async () => {
    console.log('\n[Test] Run actor with maxItems limit')

    const maxItems = 3

    const result = (await adapter.call(
      'runActor',
      {
        actorId: 'apify/hello-world',
        input: {
          outputDatasetItems: 10
        },
        maxItems: maxItems,
        timeoutSecs: 60,
        pollIntervalSecs: 5
      },
      credentials,
      context
    )) as RunActorResult

    console.log(`✓ Actor completed: ${result.status}`)
    console.log(`✓ Items returned (limited): ${result.totalCount}`)

    expect(result).toBeDefined()
    expect(result.status).toBe('SUCCEEDED')
    expect(result.items).toBeInstanceOf(Array)
    expect(result.items.length).toBeLessThanOrEqual(maxItems)
  })

  it('should handle timeout gracefully', { timeout: 15000 }, async () => {
    console.log('\n[Test] Handle actor timeout')

    // Use very short timeout to trigger TIMED_OUT status
    const result = (await adapter.call(
      'runActor',
      {
        actorId: 'apify/hello-world',
        input: {},
        timeoutSecs: 1, // Very short timeout
        pollIntervalSecs: 1
      },
      credentials,
      context
    )) as RunActorResult

    console.log(`✓ Actor status: ${result.status}`)
    console.log(`✓ Timeout handled gracefully`)

    expect(result).toBeDefined()
    // Actor might succeed, timeout, or fail depending on external state
    expect(['SUCCEEDED', 'TIMED_OUT', 'FAILED']).toContain(result.status)

    if (result.status === 'TIMED_OUT') {
      expect(result.items).toEqual([])
      expect(result.totalCount).toBe(0)
      console.log('✓ TIMED_OUT status returns empty items as expected')
    }
  })

  it('should handle invalid actor ID gracefully', async () => {
    console.log('\n[Test] Handle invalid actor ID')

    await expect(
      adapter.call(
        'runActor',
        {
          actorId: 'invalid/nonexistent-actor-xyz-123',
          input: {},
          timeoutSecs: 30,
          pollIntervalSecs: 5
        },
        credentials,
        context
      )
    ).rejects.toThrow()

    console.log('✓ Invalid actor ID rejected as expected')
  })

  it('should handle missing actorId parameter', async () => {
    console.log('\n[Test] Handle missing actorId')

    await expect(
      adapter.call(
        'runActor',
        {
          // Missing actorId
          input: {},
          timeoutSecs: 30
        },
        credentials,
        context
      )
    ).rejects.toThrow('Missing required parameter: actorId')

    console.log('✓ Missing actorId parameter rejected as expected')
  })

  it('should handle invalid credentials gracefully', async () => {
    console.log('\n[Test] Handle invalid credentials')

    const invalidCreds = { apiToken: 'invalid-token-12345' }

    await expect(
      adapter.call(
        'runActor',
        {
          actorId: 'apify/hello-world',
          input: {},
          timeoutSecs: 30
        },
        invalidCreds,
        context
      )
    ).rejects.toThrow()

    console.log('✓ Invalid credentials rejected as expected')
  })

  it('should validate credentials correctly', () => {
    console.log('\n[Test] Validate credentials')

    // Valid credentials
    const validCreds = { apiToken: 'test-token' }
    expect(adapter.validateCredentials(validCreds)).toBe(true)

    // Invalid credentials (missing apiToken)
    const invalidCreds1 = {}
    expect(adapter.validateCredentials(invalidCreds1)).toBe(false)

    // Invalid credentials (empty apiToken)
    const invalidCreds2 = { apiToken: '' }
    expect(adapter.validateCredentials(invalidCreds2)).toBe(false)

    // Invalid credentials (wrong type)
    const invalidCreds3 = { apiToken: 123 }
    expect(adapter.validateCredentials(invalidCreds3)).toBe(false)

    console.log('✓ Credential validation working correctly')
  })
})
