import { describe, it, expect } from 'vitest'
import { refreshOAuthTokenIfExpired } from '../refresh'
import type { OAuthToken } from '../../types'

/**
 * Integration tests for OAuth token refresh with real provider APIs
 *
 * These tests use real credentials but manipulate the expiry date to force refresh.
 * This allows testing the actual OAuth provider endpoints without waiting for token expiry.
 *
 * SETUP REQUIRED:
 * 1. Ensure environment variables are set:
 *    - NOTION_CLIENT_ID
 *    - NOTION_CLIENT_SECRET
 *    - GOOGLE_OAUTH_CLIENT_ID (optional)
 *    - GOOGLE_OAUTH_CLIENT_SECRET (optional)
 *
 * 2. To get a real refresh token for testing:
 *    a. Complete OAuth flow via the app (AI Studio → Settings → Credentials)
 *    b. Retrieve the stored credential from the database
 *    c. Decrypt and extract the refresh_token
 *    d. Set it in the test below
 *
 * NOTE: These tests are skipped by default. Run with:
 * pnpm test -- refresh.integration.test.ts
 */

describe('OAuth Token Refresh Integration Tests', () => {
  // Skip these tests by default since they require real credentials
  // To run: update the token below and run `pnpm test -- refresh.integration.test.ts`
  const SKIP_INTEGRATION_TESTS = true

  describe('Notion provider (basic-auth)', () => {
    it.skipIf(SKIP_INTEGRATION_TESTS)(
      'should refresh Notion token with real API',
      async () => {
        // SETUP: Replace with a real Notion refresh token
        const realNotionToken: OAuthToken = {
          provider: 'notion',
          accessToken: 'expired-or-old-access-token', // Will be replaced after refresh
          refreshToken: 'YOUR_REAL_NOTION_REFRESH_TOKEN_HERE', // <-- UPDATE THIS
          expiresAt: new Date(Date.now() - 10 * 60 * 1000).toISOString(), // Force refresh (10 min ago)
          tokenType: 'Bearer'
        }

        // This will make a real API call to Notion's token endpoint
        const refreshedToken = await refreshOAuthTokenIfExpired(realNotionToken)

        // Verify the refresh worked
        expect(refreshedToken.provider).toBe('notion')
        expect(refreshedToken.accessToken).not.toBe(realNotionToken.accessToken)
        expect(refreshedToken.accessToken).toBeTruthy()
        expect(refreshedToken.refreshToken).toBeTruthy()

        // Verify new token will expire in the future
        const expiresAt = new Date(refreshedToken.expiresAt).getTime()
        const now = Date.now()
        expect(expiresAt).toBeGreaterThan(now)

        // Log result for debugging
        console.log('✓ Notion token refreshed successfully')
        console.log('  New access token (first 20 chars):', refreshedToken.accessToken.substring(0, 20) + '...')
        console.log('  Expires at:', refreshedToken.expiresAt)
        console.log('  Time until expiry:', Math.round((expiresAt - now) / 60000), 'minutes')
      },
      { timeout: 10000 } // 10 second timeout for API call
    )

    it.skipIf(SKIP_INTEGRATION_TESTS)(
      'should handle Notion token that is still valid',
      async () => {
        // SETUP: Replace with a real Notion refresh token
        const validNotionToken: OAuthToken = {
          provider: 'notion',
          accessToken: 'current-valid-access-token',
          refreshToken: 'YOUR_REAL_NOTION_REFRESH_TOKEN_HERE', // <-- UPDATE THIS
          expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(), // 30 min in future
          tokenType: 'Bearer'
        }

        // Should return same token without API call (still valid)
        const result = await refreshOAuthTokenIfExpired(validNotionToken)

        expect(result).toBe(validNotionToken) // Same object reference
        console.log('✓ Valid token returned unchanged (no API call made)')
      }
    )
  })

  describe('Google Sheets provider (form-encoded)', () => {
    it.skipIf(SKIP_INTEGRATION_TESTS)(
      'should refresh Google Sheets token with real API',
      async () => {
        // SETUP: Replace with a real Google Sheets refresh token
        const realGoogleToken: OAuthToken = {
          provider: 'google-sheets',
          accessToken: 'expired-or-old-access-token',
          refreshToken: 'YOUR_REAL_GOOGLE_REFRESH_TOKEN_HERE', // <-- UPDATE THIS
          expiresAt: new Date(Date.now() - 10 * 60 * 1000).toISOString(), // Force refresh
          tokenType: 'Bearer',
          scope: 'https://www.googleapis.com/auth/spreadsheets'
        }

        const refreshedToken = await refreshOAuthTokenIfExpired(realGoogleToken)

        expect(refreshedToken.provider).toBe('google-sheets')
        expect(refreshedToken.accessToken).not.toBe(realGoogleToken.accessToken)
        expect(refreshedToken.accessToken).toBeTruthy()
        expect(refreshedToken.refreshToken).toBeTruthy()

        // Verify scope is preserved
        expect(refreshedToken.scope).toBe('https://www.googleapis.com/auth/spreadsheets')

        console.log('✓ Google Sheets token refreshed successfully')
        console.log('  New access token (first 20 chars):', refreshedToken.accessToken.substring(0, 20) + '...')
        console.log('  Expires at:', refreshedToken.expiresAt)
      },
      { timeout: 10000 }
    )
  })

  describe('Error scenarios with real API', () => {
    it.skipIf(SKIP_INTEGRATION_TESTS)(
      'should handle invalid refresh token with real Notion API',
      async () => {
        const tokenWithInvalidRefreshToken: OAuthToken = {
          provider: 'notion',
          accessToken: 'old-token',
          refreshToken: 'completely-invalid-refresh-token-12345', // Invalid token
          expiresAt: new Date(Date.now() - 10 * 60 * 1000).toISOString(),
          tokenType: 'Bearer'
        }

        // Should throw error from Notion API
        await expect(refreshOAuthTokenIfExpired(tokenWithInvalidRefreshToken)).rejects.toThrow()

        console.log('✓ Invalid refresh token correctly rejected by Notion API')
      },
      { timeout: 10000 }
    )
  })
})

/**
 * HELPER: Script to test with a credential from the database
 *
 * To use this with a real stored credential:
 *
 * 1. Get credential from database:
 *    ```sql
 *    SELECT * FROM credentials
 *    WHERE name = 'notion-prod'
 *    AND organization_id = 'your-org-id';
 *    ```
 *
 * 2. Decrypt the encrypted_value using CredentialsService
 *
 * 3. Extract the token object
 *
 * 4. Manipulate the expiresAt field:
 *    ```typescript
 *    const token = decryptedCredential as OAuthToken
 *    const expiredToken = {
 *      ...token,
 *      expiresAt: new Date(Date.now() - 10 * 60 * 1000).toISOString()
 *    }
 *    ```
 *
 * 5. Test refresh:
 *    ```typescript
 *    const refreshed = await refreshOAuthTokenIfExpired(expiredToken)
 *    ```
 *
 * 6. Save refreshed token back to database:
 *    ```typescript
 *    await credentialsService.updateCredential(orgId, credentialName, refreshed)
 *    ```
 */

describe('Real Credential Testing Pattern', () => {
  it('demonstrates how to test with database credentials', async () => {
    // This is a documentation test showing the pattern
    // Not meant to run - just shows the workflow

    // Step 1: Load credential from database (pseudo-code)
    // const credential = await credentialsService.getCredential(orgId, 'notion-prod')
    // const token = credential as OAuthToken

    // Step 2: Manipulate expiry to force refresh
    // const mockToken: OAuthToken = {
    //   provider: 'notion',
    //   accessToken: 'secret_abc123...', // Real token from DB
    //   refreshToken: 'secret_refresh_xyz...', // Real refresh token from DB
    //   expiresAt: new Date(Date.now() - 10 * 60 * 1000).toISOString(), // MANIPULATED: 10 min ago
    //   tokenType: 'Bearer'
    // }

    // Step 3: Test refresh (would make real API call if not skipped)
    // const refreshed = await refreshOAuthTokenIfExpired(mockToken)

    // Step 4: Verify and optionally save back
    // expect(refreshed.accessToken).not.toBe(mockToken.accessToken)
    // await credentialsService.updateCredential(orgId, 'notion-prod', refreshed)

    // This test is just documentation - always passes
    expect(true).toBe(true)
  })
})

/**
 * MANUAL TESTING SCRIPT
 *
 * To manually test token refresh with a real credential:
 *
 * 1. Create a test file: packages/core/src/integrations/oauth/server/__tests__/manual-refresh-test.ts
 *
 * 2. Add this code:
 *
 * ```typescript
 * import { refreshOAuthTokenIfExpired } from '../refresh'
 * import type { OAuthToken } from '../../types'
 *
 * async function testRefresh() {
 *   // Get a real token from your database
 *   const realToken: OAuthToken = {
 *     provider: 'notion',
 *     accessToken: 'paste-real-access-token-here',
 *     refreshToken: 'paste-real-refresh-token-here',
 *     expiresAt: new Date(Date.now() - 10 * 60 * 1000).toISOString(), // Force refresh
 *     tokenType: 'Bearer'
 *   }
 *
 *   console.log('Original token:', {
 *     accessToken: realToken.accessToken.substring(0, 20) + '...',
 *     expiresAt: realToken.expiresAt
 *   })
 *
 *   try {
 *     const refreshed = await refreshOAuthTokenIfExpired(realToken)
 *
 *     console.log('Refreshed token:', {
 *       accessToken: refreshed.accessToken.substring(0, 20) + '...',
 *       expiresAt: refreshed.expiresAt,
 *       changed: refreshed.accessToken !== realToken.accessToken
 *     })
 *
 *     console.log('✓ Token refresh successful!')
 *   } catch (error) {
 *     console.error('✗ Token refresh failed:', error)
 *   }
 * }
 *
 * testRefresh()
 * ```
 *
 * 3. Run with: pnpm tsx packages/core/src/integrations/oauth/server/__tests__/manual-refresh-test.ts
 */
