/**
 * Sovereign Identity Service (Phoenix Identity Spine)
 * Keycloak-based identity management - NO Azure dependencies
 * Superior to Azure AD with more flexible authentication flows
 */

import { getDb } from '../db/index.js'
import { logger } from '../lib/logger.js'
import jwt from 'jsonwebtoken'
import fetch from 'node-fetch'

export interface KeycloakConfig {
  url: string
  realm: string
  clientId: string
  clientSecret: string
}

export interface TenantIdentity {
  tenantId: string
  userId: string
  email: string
  roles: string[]
  permissions: Record<string, any>
  realm: string
}

export interface TokenValidationResult {
  valid: boolean
  tenantId?: string
  userId?: string
  email?: string
  roles?: string[]
  permissions?: Record<string, any>
  error?: string
}

class IdentityService {
  private keycloakConfig: KeycloakConfig | null = null
  private tokenCache: Map<string, { token: string; expiresAt: number }> = new Map()

  /**
   * Initialize Keycloak configuration
   */
  initialize(): void {
    const keycloakUrl = process.env.KEYCLOAK_URL
    const keycloakRealm = process.env.KEYCLOAK_REALM || 'master'
    const keycloakClientId = process.env.KEYCLOAK_CLIENT_ID
    const keycloakClientSecret = process.env.KEYCLOAK_CLIENT_SECRET

    if (!keycloakUrl || !keycloakClientId || !keycloakClientSecret) {
      logger.warn('Keycloak not fully configured - identity service will use fallback authentication')
      return
    }

    this.keycloakConfig = {
      url: keycloakUrl,
      realm: keycloakRealm,
      clientId: keycloakClientId,
      clientSecret: keycloakClientSecret,
    }

    logger.info('Keycloak identity service initialized', {
      url: keycloakUrl,
      realm: keycloakRealm,
    })
  }

  /**
   * Get service account token for Keycloak API calls
   */
  private async getServiceAccountToken(): Promise<string | null> {
    if (!this.keycloakConfig) {
      return null
    }

    const cacheKey = 'service-account'
    const cached = this.tokenCache.get(cacheKey)
    if (cached && cached.expiresAt > Date.now()) {
      return cached.token
    }

    try {
      const tokenUrl = `${this.keycloakConfig.url}/realms/${this.keycloakConfig.realm}/protocol/openid-connect/token`
      const response = await fetch(tokenUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
          grant_type: 'client_credentials',
          client_id: this.keycloakConfig.clientId,
          client_secret: this.keycloakConfig.clientSecret,
        }),
      })

      if (!response.ok) {
        throw new Error(`Keycloak token request failed: ${response.statusText}`)
      }

      const data = (await response.json()) as {
        access_token: string
        expires_in: number
      }

      const expiresAt = Date.now() + (data.expires_in - 60) * 1000 // 60s buffer
      this.tokenCache.set(cacheKey, {
        token: data.access_token,
        expiresAt,
      })

      return data.access_token
    } catch (error) {
      logger.error('Failed to get service account token', { error })
      return null
    }
  }

  /**
   * Validate JWT token from Keycloak
   */
  async validateToken(token: string): Promise<TokenValidationResult> {
    if (!this.keycloakConfig) {
      // Fallback to JWT validation if Keycloak not configured
      return this.validateJWTFallback(token)
    }

    try {
      // For now, use introspection endpoint for token validation
      // In production, use JWKS with proper library like jose
      const introspectUrl = `${this.keycloakConfig.url}/realms/${this.keycloakConfig.realm}/protocol/openid-connect/token/introspect`
      const response = await fetch(introspectUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
          token,
          client_id: this.keycloakConfig.clientId,
          client_secret: this.keycloakConfig.clientSecret,
        }),
      })

      if (!response.ok) {
        // Fallback to JWT validation if introspection fails
        return this.validateJWTFallback(token)
      }

      const data = (await response.json()) as {
        active: boolean
        sub?: string
        email?: string
        preferred_username?: string
        realm_access?: { roles?: string[] }
        tenant_id?: string
        'https://sankofa.nexus/tenant_id'?: string
        permissions?: Record<string, any>
      }

      if (!data.active) {
        return { valid: false, error: 'Token is not active' }
      }

      // Extract tenant and user information
      const tenantId = data.tenant_id || data['https://sankofa.nexus/tenant_id']
      const userId = data.sub
      const email = data.email || data.preferred_username
      const roles = data.realm_access?.roles || []
      const permissions = data.permissions || {}

      // Get tenant from database to verify
      if (tenantId) {
        const db = getDb()
        const tenantResult = await db.query(
          'SELECT id FROM tenants WHERE id = $1 AND status = $2',
          [tenantId, 'ACTIVE']
        )

        if (tenantResult.rows.length === 0) {
          return { valid: false, error: 'Tenant not found or inactive' }
        }
      }

      return {
        valid: true,
        tenantId: tenantId || undefined,
        userId,
        email,
        roles,
        permissions,
      }
    } catch (error) {
      logger.error('Token validation failed', { error })
      // Fallback to JWT validation
      return this.validateJWTFallback(token)
    }
  }

  /**
   * Fallback JWT validation when Keycloak is not configured
   */
  private validateJWTFallback(token: string): TokenValidationResult {
    try {
      const JWT_SECRET = process.env.JWT_SECRET || 'dev-secret-change-in-production'
      const decoded = jwt.verify(token, JWT_SECRET) as jwt.JwtPayload

      return {
        valid: true,
        tenantId: decoded.tenantId || decoded.tenant_id,
        userId: decoded.id || decoded.sub,
        email: decoded.email,
        roles: decoded.roles ? (Array.isArray(decoded.roles) ? decoded.roles : [decoded.roles]) : [],
        permissions: decoded.permissions || {},
      }
    } catch (error) {
      return { valid: false, error: 'Invalid token' }
    }
  }

  /**
   * Get user's tenant memberships
   */
  async getUserTenants(userId: string): Promise<string[]> {
    const db = getDb()
    const result = await db.query(
      `SELECT tenant_id FROM tenant_users WHERE user_id = $1`,
      [userId]
    )
    return result.rows.map((row) => row.tenant_id)
  }

  /**
   * Get tenant users
   */
  async getTenantUsers(tenantId: string): Promise<any[]> {
    const db = getDb()
    const result = await db.query(
      `SELECT tu.*, u.email, u.name 
       FROM tenant_users tu
       JOIN users u ON tu.user_id = u.id
       WHERE tu.tenant_id = $1`,
      [tenantId]
    )
    return result.rows
  }

  /**
   * Add user to tenant
   */
  async addUserToTenant(
    tenantId: string,
    userId: string,
    role: string,
    permissions?: Record<string, any>
  ): Promise<void> {
    const db = getDb()
    await db.query(
      `INSERT INTO tenant_users (tenant_id, user_id, role, permissions)
       VALUES ($1, $2, $3, $4)
       ON CONFLICT (tenant_id, user_id) 
       DO UPDATE SET role = $3, permissions = $4, updated_at = NOW()`,
      [tenantId, userId, role, JSON.stringify(permissions || {})]
    )
  }

  /**
   * Remove user from tenant
   */
  async removeUserFromTenant(tenantId: string, userId: string): Promise<void> {
    const db = getDb()
    await db.query(`DELETE FROM tenant_users WHERE tenant_id = $1 AND user_id = $2`, [
      tenantId,
      userId,
    ])
  }

  /**
   * Create tenant realm in Keycloak (if multi-realm support is enabled)
   */
  async createTenantRealm(tenantId: string, tenantName: string): Promise<void> {
    if (!this.keycloakConfig) {
      logger.warn('Keycloak not configured - skipping realm creation')
      return
    }

    const serviceToken = await this.getServiceAccountToken()
    if (!serviceToken) {
      throw new Error('Failed to get service account token')
    }

    try {
      const realmUrl = `${this.keycloakConfig.url}/admin/realms`
      const response = await fetch(realmUrl, {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${serviceToken}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          realm: tenantId,
          enabled: true,
          displayName: tenantName,
        }),
      })

      if (!response.ok) {
        const error = await response.text()
        throw new Error(`Failed to create realm: ${error}`)
      }

      logger.info('Created tenant realm in Keycloak', { tenantId, tenantName })
    } catch (error) {
      logger.error('Failed to create tenant realm', { error, tenantId })
      throw error
    }
  }

  /**
   * Verify blockchain identity (unique to Phoenix)
   * Integrates with Enterprise Ethereum Alliance (EEA) blockchain
   */
  async verifyBlockchainIdentity(
    userId: string,
    blockchainAddress: string
  ): Promise<boolean> {
    try {
      const { blockchainService } = await import('./blockchain.js')
      const result = await blockchainService.verifyIdentity(userId, blockchainAddress)
      
      if (!result.verified) {
        logger.warn('Blockchain identity verification failed', {
          userId,
          blockchainAddress,
          error: result.error,
        })
      }
      
      return result.verified
    } catch (error) {
      logger.error('Blockchain identity verification error', {
        userId,
        blockchainAddress,
        error,
      })
      // Fail securely - return false on error
      return false
    }
  }
}

// Singleton instance
export const identityService = new IdentityService()

// Initialize on module load
identityService.initialize()

