/**
 * Blockchain Lifecycle Manager
 * Manages Hyperledger stack deployments (Fabric, Besu, Indy, FireFly)
 */

import { getDb } from '../db/index.js'
import { logger } from '../lib/logger.js'
import { Context } from '../types/context.js'
import { deploymentService } from './deployment.js'

export enum BlockchainFramework {
  HYPERLEDGER_FABRIC = 'HYPERLEDGER_FABRIC',
  HYPERLEDGER_BESU = 'HYPERLEDGER_BESU',
  HYPERLEDGER_INDY = 'HYPERLEDGER_INDY',
  HYPERLEDGER_FIREFLY = 'HYPERLEDGER_FIREFLY',
  CACTI = 'CACTI',
}

export enum NodeRole {
  PEER = 'PEER',
  ORDERER = 'ORDERER',
  VALIDATOR = 'VALIDATOR',
  ENDORSER = 'ENDORSER',
  CA = 'CA',
  IDENTITY_NODE = 'IDENTITY_NODE',
  FIREFLY_NODE = 'FIREFLY_NODE',
  CACTI_NODE = 'CACTI_NODE',
}

export interface BlockchainNetwork {
  id: string
  name: string
  framework: BlockchainFramework
  deploymentId?: string
  networkId?: string
  status: string
  configuration: Record<string, any>
  metadata: Record<string, any>
  createdAt: Date
  updatedAt: Date
}

export interface BlockchainNode {
  id: string
  networkId: string
  name: string
  role: NodeRole
  nodeType?: string
  endpointUrl?: string
  rpcEndpoint?: string
  grpcEndpoint?: string
  websocketEndpoint?: string
  status: string
  configuration: Record<string, any>
  createdAt: Date
  updatedAt: Date
}

export interface CreateBlockchainNetworkInput {
  name: string
  framework: BlockchainFramework
  region?: string
  configuration: {
    nodeRoles: Array<{
      role: NodeRole
      count: number
    }>
    ledgerStorage?: string
    consensus?: string
    permissioning?: Record<string, any>
  }
}

class BlockchainLifecycleManager {
  /**
   * Create blockchain network
   */
  async createNetwork(
    context: Context,
    input: CreateBlockchainNetworkInput
  ): Promise<BlockchainNetwork> {
    logger.info('Creating blockchain network', {
      name: input.name,
      framework: input.framework,
    })

    const db = getDb()

    // Create deployment for blockchain network
    const deployment = await deploymentService.createDeployment(context, {
      name: input.name,
      templateId: await this.getTemplateIdForFramework(input.framework),
      region: input.region,
      deploymentType: 'KUBERNETES',
      parameters: {
        framework: input.framework,
        ...input.configuration,
      },
    })

    // Create network record
    const result = await db.query(
      `INSERT INTO blockchain_networks (
        name, framework, deployment_id, status, configuration
      ) VALUES ($1, $2, $3, $4, $5)
      RETURNING *`,
      [
        input.name,
        input.framework,
        deployment.id,
        'PROVISIONING',
        JSON.stringify(input.configuration),
      ]
    )

    const network = this.mapNetwork(result.rows[0])

    // Create nodes based on configuration
    for (const nodeRole of input.configuration.nodeRoles) {
      for (let i = 0; i < nodeRole.count; i++) {
        await this.createNode(context, network.id, {
          name: `${input.name}-${nodeRole.role.toLowerCase()}-${i + 1}`,
          role: nodeRole.role,
          configuration: {},
        })
      }
    }

    logger.info('Blockchain network created', { networkId: network.id })
    return network
  }

  /**
   * Create blockchain node
   */
  async createNode(
    context: Context,
    networkId: string,
    input: {
      name: string
      role: NodeRole
      nodeType?: string
      configuration?: Record<string, any>
    }
  ): Promise<BlockchainNode> {
    const db = getDb()

    const result = await db.query(
      `INSERT INTO blockchain_nodes (
        network_id, name, role, node_type, status, configuration
      ) VALUES ($1, $2, $3, $4, $5, $6)
      RETURNING *`,
      [
        networkId,
        input.name,
        input.role,
        input.nodeType || 'KUBERNETES',
        'PENDING',
        JSON.stringify(input.configuration || {}),
      ]
    )

    return this.mapNode(result.rows[0])
  }

  /**
   * Get network by ID
   */
  async getNetwork(context: Context, id: string): Promise<BlockchainNetwork | null> {
    const db = getDb()
    const result = await db.query(
      `SELECT * FROM blockchain_networks WHERE id = $1`,
      [id]
    )
    if (result.rows.length === 0) return null
    return this.mapNetwork(result.rows[0])
  }

  /**
   * Get nodes for network
   */
  async getNodes(context: Context, networkId: string): Promise<BlockchainNode[]> {
    const db = getDb()
    const result = await db.query(
      `SELECT * FROM blockchain_nodes WHERE network_id = $1 ORDER BY created_at`,
      [networkId]
    )
    return result.rows.map(this.mapNode)
  }

  /**
   * Get template ID for framework
   */
  private async getTemplateIdForFramework(framework: BlockchainFramework): Promise<string> {
    // In production, this would look up the template from catalog
    const templateMap: Record<BlockchainFramework, string> = {
      [BlockchainFramework.HYPERLEDGER_FABRIC]: 'fabric-template-id',
      [BlockchainFramework.HYPERLEDGER_BESU]: 'besu-template-id',
      [BlockchainFramework.HYPERLEDGER_INDY]: 'indy-template-id',
      [BlockchainFramework.HYPERLEDGER_FIREFLY]: 'firefly-template-id',
      [BlockchainFramework.CACTI]: 'cacti-template-id',
    }
    return templateMap[framework]
  }

  // Mapper functions
  private mapNetwork(row: any): BlockchainNetwork {
    return {
      id: row.id,
      name: row.name,
      framework: row.framework as BlockchainFramework,
      deploymentId: row.deployment_id,
      networkId: row.network_id,
      status: row.status,
      configuration: row.configuration || {},
      metadata: row.metadata || {},
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    }
  }

  private mapNode(row: any): BlockchainNode {
    return {
      id: row.id,
      networkId: row.network_id,
      name: row.name,
      role: row.role as NodeRole,
      nodeType: row.node_type,
      endpointUrl: row.endpoint_url,
      rpcEndpoint: row.rpc_endpoint,
      grpcEndpoint: row.grpc_endpoint,
      websocketEndpoint: row.websocket_endpoint,
      status: row.status,
      configuration: row.configuration || {},
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    }
  }
}

export const blockchainLifecycleManager = new BlockchainLifecycleManager()

