/**
 * Provisioning Service
 * Executes inline provisioning via SSH/Ansible
 */
import { prisma } from '@/lib/prisma'
import { OrderStatus, LogLevel } from '@prisma/client'
import { runProvisioning } from '@/lib/ansible/runner'
import { AnsibleConfig, AnsibleLogEvent } from '@/lib/ansible/types'
import { SSH_PORT_AFTER_PROVISION } from '@/lib/ssh/constants'
import { decryptPassword } from '@/lib/services/config-generator'

export interface ProvisioningResult {
  success: boolean
  error?: string
  duration: number
}

/**
 * Map AnsibleLogEvent type to database LogLevel
 */
function toLogLevel(eventType: AnsibleLogEvent['type']): LogLevel {
  switch (eventType) {
    case 'error':
      return LogLevel.ERROR
    case 'stderr':
      return LogLevel.WARN
    default:
      return LogLevel.INFO
  }
}

/**
 * Execute provisioning for an order
 * This runs inline and streams logs to the ProvisioningLog table
 */
export async function executeProvisioning(
  orderId: string,
  registrationToken: string
): Promise<ProvisioningResult> {
  const startTime = Date.now()

  // Get order with all required data
  const order = await prisma.order.findUnique({
    where: { id: orderId },
    include: {
      user: true,
    },
  })

  if (!order) {
    return {
      success: false,
      error: 'Order not found',
      duration: Date.now() - startTime,
    }
  }

  if (!order.serverIp || !order.serverPasswordEncrypted) {
    return {
      success: false,
      error: 'Server credentials not configured',
      duration: Date.now() - startTime,
    }
  }

  // Decrypt password
  let password: string
  try {
    password = decryptPassword(order.serverPasswordEncrypted)
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : 'Password decryption failed'
    return {
      success: false,
      error: errorMessage,
      duration: Date.now() - startTime,
    }
  }

  // Build Ansible config from order
  const hubUrl = process.env.HUB_URL || process.env.NEXTAUTH_URL || 'http://localhost:3000'

  const config: AnsibleConfig = {
    serverIp: order.serverIp,
    serverPort: order.sshPort,
    password,
    domain: order.domain,
    adminEmail: order.user.email,
    tools: order.tools,
    hubUrl,
    registrationToken,
    orchestratorVersion: 'latest',
    agentVersion: 'latest',
  }

  // Create log callback that writes to ProvisioningLog table
  const logCallback = async (event: AnsibleLogEvent): Promise<void> => {
    try {
      await prisma.provisioningLog.create({
        data: {
          orderId,
          level: toLogLevel(event.type),
          message: event.message,
          step: event.step || null,
        },
      })
    } catch (logError) {
      // Don't fail provisioning if logging fails
      console.error('Failed to write provisioning log:', logError)
    }
  }

  try {
    // Update order status to PROVISIONING
    await prisma.order.update({
      where: { id: orderId },
      data: {
        status: OrderStatus.PROVISIONING,
        provisioningStartedAt: new Date(),
      },
    })

    // Log start
    await logCallback({
      type: 'status',
      message: `Starting provisioning for ${order.domain} (${order.serverIp})`,
      step: 'init',
      timestamp: new Date(),
    })

    // Run the actual provisioning
    const result = await runProvisioning(config, logCallback)

    if (result.success) {
      // Update order to FULFILLED with new SSH port
      await prisma.order.update({
        where: { id: orderId },
        data: {
          status: OrderStatus.FULFILLED,
          completedAt: new Date(),
          // Update SSH port after provisioning (server hardens SSH to custom port)
          sshPort: SSH_PORT_AFTER_PROVISION,
          // Clear sensitive data
          serverPasswordEncrypted: null,
        },
      })

      await logCallback({
        type: 'complete',
        message: `Provisioning completed successfully in ${Math.round(result.duration / 1000)}s`,
        step: 'complete',
        progress: 100,
        timestamp: new Date(),
      })

      return {
        success: true,
        duration: result.duration,
      }
    } else {
      // Update order to FAILED
      await prisma.order.update({
        where: { id: orderId },
        data: {
          status: OrderStatus.FAILED,
          failureReason: result.error || 'Provisioning failed',
        },
      })

      return {
        success: false,
        error: result.error || 'Provisioning failed',
        duration: result.duration,
      }
    }
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : 'Unknown error'

    // Update order to FAILED
    await prisma.order.update({
      where: { id: orderId },
      data: {
        status: OrderStatus.FAILED,
        failureReason: errorMessage,
      },
    })

    await logCallback({
      type: 'error',
      message: `Provisioning failed: ${errorMessage}`,
      step: 'error',
      timestamp: new Date(),
    })

    return {
      success: false,
      error: errorMessage,
      duration: Date.now() - startTime,
    }
  }
}
