#!/usr/bin/env nu

# Integration Functions for External Systems
#
# Provides integration with:
# - MCP (Model Context Protocol) servers
# - Rust installer binary
# - REST APIs
# - Webhook notifications

# Load configuration from MCP server
#
# Queries the MCP server for deployment configuration using
# the Model Context Protocol.
#
# @param mcp_url: MCP server URL
# @returns: Deployment configuration record
export def load-config-from-mcp [mcp_url: string]: nothing -> record {
    print $"📡 Loading configuration from MCP server: ($mcp_url)"

    # MCP request payload
    let request = {
        jsonrpc: "2.0"
        id: 1
        method: "config/get"
        params: {
            type: "deployment"
            include_defaults: true
        }
    }

    # Call MCP server (no try-catch)
    let post_result = (do { http post $mcp_url --content-type "application/json" ($request | to json) } | complete)
    if $post_result.exit_code != 0 {
        error make {
            msg: $"Failed to load config from MCP: ($mcp_url)"
            label: {text: $post_result.stderr}
            help: "Ensure MCP server is running and accessible"
        }
    }

    let response = ($post_result.stdout)

    if "error" in ($response | columns) {
        error make {
            msg: $"MCP error: ($response.error.message)"
            label: {text: $"Code: ($response.error.code)"}
        }
    }

    if "result" not-in ($response | columns) {
        error make {msg: "Invalid MCP response: missing result"}
    }

    print "✅ Configuration loaded from MCP server"
    $response.result
}

# Load configuration from REST API
#
# Fetches deployment configuration from a REST API endpoint.
#
# @param api_url: API endpoint URL
# @returns: Deployment configuration record
export def load-config-from-api [api_url: string]: nothing -> record {
    print $"🌐 Loading configuration from API: ($api_url)"

    # Call API (no try-catch)
    let get_result = (do { http get $api_url --max-time 30sec } | complete)
    if $get_result.exit_code != 0 {
        error make {
            msg: $"Failed to load config from API: ($api_url)"
            label: {text: $get_result.stderr}
            help: "Check API endpoint and network connectivity"
        }
    }

    let response = ($get_result.stdout)

    if "config" not-in ($response | columns) {
        error make {msg: "Invalid API response: missing 'config' field"}
    }

    print "✅ Configuration loaded from API"
    $response.config
}

# Send notification to webhook
#
# Sends deployment event notifications to a configured webhook URL.
# Useful for integration with Slack, Discord, Microsoft Teams, etc.
#
# @param webhook_url: Webhook URL
# @param payload: Notification payload record
# @returns: Nothing
export def notify-webhook [webhook_url: string, payload: record]: nothing -> nothing {
    # Send webhook notification (no try-catch, graceful error handling)
    let post_result = (do { http post $webhook_url --content-type "application/json" ($payload | to json) } | complete)
    if $post_result.exit_code != 0 {
        # Don't fail deployment on webhook errors, just log
        print $"⚠️  Warning: Failed to send webhook notification: ($post_result.stderr)"
    }

    null
}

# Call Rust installer binary with arguments
#
# Invokes the Rust installer binary with specified arguments,
# capturing output and exit code.
#
# @param args: List of arguments to pass to installer
# @returns: Installer execution result record
export def call-installer [args: list<string>]: nothing -> record {
    let installer_path = get-installer-path

    print $"🚀 Calling installer: ($installer_path) ($args | str join ' ')"

    # Execute installer binary (no try-catch)
    let output = (do { ^$installer_path ...$args } | complete)

    {
        success: ($output.exit_code == 0)
        exit_code: $output.exit_code
        stdout: $output.stdout
        stderr: $output.stderr
        timestamp: (date now)
    }
}

# Run installer in headless mode with config file
#
# Executes the Rust installer in headless mode using a
# configuration file.
#
# @param config_path: Path to configuration file
# @param auto_confirm: Auto-confirm prompts
# @returns: Installer execution result record
export def run-installer-headless [
    config_path: path
    --auto-confirm
]: nothing -> record {
    mut args = ["--headless", "--config", $config_path]

    if $auto_confirm {
        $args = ($args | append "--yes")
    }

    call-installer $args
}

# Run installer interactively
#
# Launches the Rust installer in interactive TUI mode.
#
# @returns: Installer execution result record
export def run-installer-interactive []: nothing -> record {
    let installer_path = get-installer-path

    print $"🚀 Launching interactive installer: ($installer_path)"

    # Run interactive installer (no try-catch)
    let result = (do { ^$installer_path } | complete)

    if $result.exit_code == 0 {
        {
            success: true
            mode: "interactive"
            message: "Interactive installer completed"
            timestamp: (date now)
        }
    } else {
        {
            success: false
            mode: "interactive"
            error: $result.stderr
            timestamp: (date now)
        }
    }
}

# Pass deployment config to installer via CLI args
#
# Converts a deployment configuration record into CLI arguments
# for the Rust installer binary.
#
# @param config: Deployment configuration record
# @returns: List of CLI arguments
export def config-to-cli-args [config: record]: nothing -> list<string> {
    mut args = ["--headless"]

    # Add platform
    $args = ($args | append ["--platform", $config.platform])

    # Add mode
    $args = ($args | append ["--mode", $config.mode])

    # Add domain
    $args = ($args | append ["--domain", $config.domain])

    # Add services (comma-separated)
    let services = $config.services
        | where enabled
        | get name
        | str join ","

    if $services != "" {
        $args = ($args | append ["--services", $services])
    }

    $args
}

# Deploy using installer binary
#
# High-level function to deploy using the Rust installer binary
# with the given configuration.
#
# @param config: Deployment configuration record
# @param auto_confirm: Auto-confirm prompts
# @returns: Deployment result record
export def deploy-with-installer [
    config: record
    --auto-confirm
]: nothing -> record {
    print "🚀 Deploying using Rust installer binary..."

    # Convert config to CLI args
    mut args = (config-to-cli-args $config)

    if $auto_confirm {
        $args = ($args | append "--yes")
    }

    # Execute installer
    let result = call-installer $args

    if $result.success {
        print "✅ Installer deployment successful"
        {
            success: true
            method: "installer_binary"
            config: $config
            timestamp: (date now)
        }
    } else {
        print $"❌ Installer deployment failed: ($result.stderr)"
        {
            success: false
            method: "installer_binary"
            error: $result.stderr
            exit_code: $result.exit_code
            timestamp: (date now)
        }
    }
}

# Query MCP server for deployment status
#
# Retrieves deployment status information from MCP server.
#
# @param mcp_url: MCP server URL
# @param deployment_id: Deployment identifier
# @returns: Deployment status record
export def query-mcp-status [mcp_url: string, deployment_id: string]: nothing -> record {
    let request = {
        jsonrpc: "2.0"
        id: 1
        method: "deployment/status"
        params: {
            deployment_id: $deployment_id
        }
    }

    # Query MCP status (no try-catch)
    let post_result = (do { http post $mcp_url --content-type "application/json" ($request | to json) } | complete)
    if $post_result.exit_code != 0 {
        error make {
            msg: $"Failed to query MCP status: ($post_result.stderr)"
        }
    }

    let response = ($post_result.stdout)

    if "error" in ($response | columns) {
        error make {
            msg: $"MCP error: ($response.error.message)"
        }
    }

    $response.result
}

# Register deployment with API
#
# Registers a new deployment with the external API and returns
# a deployment ID for tracking.
#
# @param api_url: API endpoint URL
# @param config: Deployment configuration
# @returns: Registration result with deployment ID
export def register-deployment-with-api [api_url: string, config: record]: nothing -> record {
    let payload = {
        platform: $config.platform
        mode: $config.mode
        domain: $config.domain
        services: ($config.services | get name)
        started_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
    }

    # Register deployment with API (no try-catch)
    let post_result = (do { http post $api_url --content-type "application/json" ($payload | to json) } | complete)
    if $post_result.exit_code != 0 {
        print $"⚠️  Warning: Failed to register with API: ($post_result.stderr)"
        return {
            success: false
            error: $post_result.stderr
        }
    }

    let response = ($post_result.stdout)

    if "deployment_id" not-in ($response | columns) {
        print "⚠️  Warning: API did not return deployment_id"
        return {
            success: false
            error: "API did not return deployment_id"
        }
    }

    print $"✅ Deployment registered with API: ($response.deployment_id)"

    {
        success: true
        deployment_id: $response.deployment_id
        api_url: $api_url
    }
}

# Update deployment status via API
#
# Updates deployment status on external API for tracking and monitoring.
#
# @param api_url: API endpoint URL
# @param deployment_id: Deployment identifier
# @param status: Status update record
# @returns: Update result record
export def update-deployment-status [
    api_url: string
    deployment_id: string
    status: record
]: nothing -> record {
    let update_url = $"($api_url)/($deployment_id)/status"

    # Update deployment status (no try-catch, graceful error handling)
    let patch_result = (do { http patch $update_url --content-type "application/json" ($status | to json) } | complete)
    if $patch_result.exit_code != 0 {
        print $"⚠️  Warning: Failed to update deployment status: ($patch_result.stderr)"
        return {success: false, error: $patch_result.stderr}
    }

    {success: true}
}

# Send Slack notification
#
# Sends formatted notification to Slack webhook.
#
# @param webhook_url: Slack webhook URL
# @param message: Message text
# @param color: Message color (good, warning, danger)
# @returns: Nothing
export def notify-slack [
    webhook_url: string
    message: string
    --color: string = "good"
]: nothing -> nothing {
    let payload = {
        attachments: [{
            color: $color
            text: $message
            footer: "Provisioning Platform Installer"
            ts: (date now | format date "%s")
        }]
    }

    notify-webhook $webhook_url $payload
}

# Send Discord notification
#
# Sends formatted notification to Discord webhook.
#
# @param webhook_url: Discord webhook URL
# @param message: Message text
# @param success: Whether deployment was successful
# @returns: Nothing
export def notify-discord [
    webhook_url: string
    message: string
    --success
]: nothing -> nothing {
    let color = if $success { 3066993 } else { 15158332 }  # Green or Red
    let emoji = if $success { "✅" } else { "❌" }

    let payload = {
        embeds: [{
            title: $"($emoji) Provisioning Platform Deployment"
            description: $message
            color: $color
            timestamp: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
            footer: {
                text: "Provisioning Platform Installer"
            }
        }]
    }

    notify-webhook $webhook_url $payload
}

# Send Microsoft Teams notification
#
# Sends formatted notification to Microsoft Teams webhook.
#
# @param webhook_url: Teams webhook URL
# @param title: Notification title
# @param message: Message text
# @param success: Whether deployment was successful
# @returns: Nothing
export def notify-teams [
    webhook_url: string
    title: string
    message: string
    --success
]: nothing -> nothing {
    let theme_color = if $success { "00FF00" } else { "FF0000" }

    let payload = {
        "@type": "MessageCard"
        "@context": "https://schema.org/extensions"
        summary: $title
        themeColor: $theme_color
        title: $title
        text: $message
    }

    notify-webhook $webhook_url $payload
}

# Execute MCP tool call
#
# Executes a tool/function call via MCP server.
#
# @param mcp_url: MCP server URL
# @param tool_name: Name of tool to execute
# @param arguments: Tool arguments record
# @returns: Tool execution result
export def execute-mcp-tool [
    mcp_url: string
    tool_name: string
    arguments: record
]: nothing -> record {
    let request = {
        jsonrpc: "2.0"
        id: 1
        method: "tools/call"
        params: {
            name: $tool_name
            arguments: $arguments
        }
    }

    # Execute MCP tool (no try-catch)
    let post_result = (do { http post $mcp_url --content-type "application/json" ($request | to json) } | complete)
    if $post_result.exit_code != 0 {
        error make {
            msg: $"Failed to execute MCP tool: ($post_result.stderr)"
        }
    }

    let response = ($post_result.stdout)

    if "error" in ($response | columns) {
        error make {
            msg: $"MCP tool execution error: ($response.error.message)"
        }
    }

    $response.result
}

# Get installer binary path (helper function)
#
# @returns: Path to installer binary
def get-installer-path []: nothing -> path {
    let installer_dir = $env.PWD | path dirname
    let installer_name = if $nu.os-info.name == "windows" {
        "provisioning-installer.exe"
    } else {
        "provisioning-installer"
    }

    # Check target/release first, then target/debug
    let release_path = $installer_dir | path join "target" "release" $installer_name
    let debug_path = $installer_dir | path join "target" "debug" $installer_name

    if ($release_path | path exists) {
        $release_path
    } else if ($debug_path | path exists) {
        $debug_path
    } else {
        error make {
            msg: "Installer binary not found"
            help: "Build with: cargo build --release"
        }
    }
}
