#!/usr/bin/env nu
# [command]
# name = "auth login"
# group = "authentication"
# tags = ["authentication", "jwt", "interactive", "login"]
# version = "3.0.0"
# requires = ["nushell:0.109.0"]

# Authentication Plugin Wrapper with HTTP Fallback
# Provides graceful degradation to HTTP API when nu_plugin_auth is unavailable

use ../config/accessor.nu *
use ../commands/traits.nu *

# Check if auth plugin is available (registered with Nushell)
def is-plugin-available [] {
    let installed = (version | get installed_plugins)
    $installed | str contains "auth"
}

# Check if auth plugin is enabled in config
def is-plugin-enabled [] {
    config-get "plugins.auth_enabled" true
}

# Get control center base URL
def get-control-center-url [] {
    config-get "platform.control_center.url" "http://localhost:3000"
}

# Store token in OS keyring (requires plugin)
def store-token-keyring [
    token: string
] {
    if (is-plugin-available) {
        # Note: auth plugin doesn't provide store-token command
        # Token storage is handled by the auth service backend
        print "⚠️ Token storage via keyring requires authentication service"
    } else {
        print "⚠️ Keyring storage unavailable (plugin not loaded)"
    }
}

# Retrieve token from OS keyring (requires plugin)
def get-token-keyring [] {
    # Token retrieval from keyring not implemented in current auth plugin
    # Check environment variable as fallback
    $env.PROVISIONING_AUTH_TOKEN? | default ""
}

# Helper to safely execute a closure and return null on error
def try-plugin [callback: closure] {
    do -i $callback
}

# Login with username and password
export def plugin-login [
    username: string
    password: string
    --mfa-code: string = ""  # Optional MFA code
] {
    let enabled = is-plugin-enabled
    let available = is-plugin-available

    if $enabled and $available {
        let plugin_result = (try-plugin {
            # Note: Plugin login command may not support MFA code directly
            # If MFA is required, it should be handled separately via mfa-verify
            let result = (auth login $username $password)
            store-token-keyring $result.access_token

            # If MFA code provided, verify it after login
            if not ($mfa_code | is-empty) {
                let mfa_result = (try-plugin {
                    auth mfa-verify $mfa_code
                })
                if $mfa_result == null {
                    print "⚠️ MFA verification failed, but login succeeded"
                }
            }

            $result
        })

        if $plugin_result != null {
            return $plugin_result
        }

        print "⚠️ Plugin login failed, falling back to HTTP"
    }

    # HTTP fallback
    print "⚠️ nu_plugin_auth not available - using HTTP fallback for authentication"
    let url = $"(get-control-center-url)/api/auth/login"

    let body = if ($mfa_code | is-empty) {
        {username: $username, password: $password}
    } else {
        {username: $username, password: $password, mfa_code: $mfa_code}
    }

    let result = (do -i {
        http post $url $body
    })

    if $result != null {
        return $result
    }

    error make {
        msg: "Login failed"
        label: {
            text: "HTTP request failed"
            span: (metadata $username).span
        }
    }
}

# Logout and revoke tokens
export def plugin-logout [] {
    let enabled = is-plugin-enabled
    let available = is-plugin-available

    let token = get-token-keyring

    if $enabled and $available {
        let plugin_result = (try-plugin {
            auth logout
        })

        if $plugin_result != null {
            return $plugin_result
        }

        print "⚠️ Plugin logout failed, falling back to HTTP"
    }

    # HTTP fallback
    print "⚠️ nu_plugin_auth not available - using HTTP fallback for authentication"
    let url = $"(get-control-center-url)/api/auth/logout"

    let result = (do -i {
        if ($token | is-empty) {
            http post $url
        } else {
            http post $url --headers {Authorization: $"Bearer ($token)"}
        }
    })

    if $result != null {
        return {success: true, message: "Logged out successfully"}
    }

    {success: false, message: "Logout failed"}

}

# Verify current authentication token
export def plugin-verify [] {
    let enabled = is-plugin-enabled
    let available = is-plugin-available
    let environment = (config-get "environment" "dev")

    if $enabled and $available {
        let plugin_result = (try-plugin {
            auth verify
        })

        if $plugin_result != null {
            return $plugin_result
        }

        # Only show warning if not in dev mode
        if $environment != "dev" {
            print "⚠️ Plugin verify failed, falling back to HTTP"
        }
    }

    # HTTP fallback - only show warning if not in dev mode
    if $environment != "dev" {
        print "⚠️ nu_plugin_auth not available - using HTTP fallback for authentication"
    }
    let token = get-token-keyring

    if ($token | is-empty) {
        return {valid: false, message: "No token found"}
    }

    let url = $"(get-control-center-url)/api/auth/verify"

    let result = (do -i {
        http get $url --headers {Authorization: $"Bearer ($token)"}
    })

    if $result != null {
        return $result
    }

    {valid: false, message: "Token verification failed"}

}

# List active sessions
export def plugin-sessions [] {
    let enabled = is-plugin-enabled
    let available = is-plugin-available

    if $enabled and $available {
        let plugin_result = (try-plugin {
            auth sessions
        })

        if $plugin_result != null {
            return $plugin_result
        }

        print "⚠️ Plugin sessions failed, falling back to HTTP"
    }

    # HTTP fallback
    print "⚠️ nu_plugin_auth not available - using HTTP fallback for authentication"
    let token = get-token-keyring

    if ($token | is-empty) {
        return []
    }

    let url = $"(get-control-center-url)/api/auth/sessions"

    let response = (do -i {
        http get $url --headers {Authorization: $"Bearer ($token)"}
    })

    if $response != null {
        return ($response | get sessions? | default [])
    }

    []

}

# Enroll MFA device (TOTP)
export def plugin-mfa-enroll [
    --type: string = "totp"  # totp or webauthn
] {
    let enabled = is-plugin-enabled
    let available = is-plugin-available

    if $enabled and $available {
        let plugin_result = (try-plugin {
            auth mfa-enroll --type $type
        })

        if $plugin_result != null {
            return $plugin_result
        }

        print "⚠️ Plugin MFA enroll failed, falling back to HTTP"
    }

    # HTTP fallback
    print "⚠️ nu_plugin_auth not available - using HTTP fallback for authentication"
    let token = get-token-keyring

    if ($token | is-empty) {
        error make {
            msg: "Authentication required"
            label: {text: "No valid token found"}
        }
    }

    let url = $"(get-control-center-url)/api/mfa/enroll"

    let result = (do -i {
        http post $url {type: $type} --headers {Authorization: $"Bearer ($token)"}
    })

    if $result != null {
        return $result
    }

    error make {
        msg: "MFA enrollment failed"
        label: {text: "HTTP request failed"}
    }
}

# Verify MFA code
export def plugin-mfa-verify [
    code: string
    --type: string = "totp"  # totp or webauthn
] {
    let enabled = is-plugin-enabled
    let available = is-plugin-available

    if $enabled and $available {
        let plugin_result = (try-plugin {
            auth mfa-verify $code --type $type
        })

        if $plugin_result != null {
            return $plugin_result
        }

        print "⚠️ Plugin MFA verify failed, falling back to HTTP"
    }

    # HTTP fallback
    print "⚠️ nu_plugin_auth not available - using HTTP fallback for authentication"
    let token = get-token-keyring

    if ($token | is-empty) {
        error make {
            msg: "Authentication required"
            label: {text: "No valid token found"}
        }
    }

    let url = $"(get-control-center-url)/api/mfa/verify"

    let result = (do -i {
        http post $url {code: $code, type: $type} --headers {Authorization: $"Bearer ($token)"}
    })

    if $result != null {
        return $result
    }

    error make {
        msg: "MFA verification failed"
        label: {
            text: "HTTP request failed"
            span: (metadata $code).span
        }
    }
}

# Get current authentication status
export def plugin-auth-status [] {
    let plugin_available = is-plugin-available
    let plugin_enabled = is-plugin-enabled
    let token = get-token-keyring
    let has_token = not ($token | is-empty)

    {
        plugin_available: $plugin_available
        plugin_enabled: $plugin_enabled
        has_token: $has_token
        mode: (if ($plugin_enabled and $plugin_available) { "plugin" } else { "http" })
    }
}

# ============================================================================
# Metadata-Driven Authentication Helpers
# ============================================================================

# Get auth requirements from metadata for a specific command
def get-metadata-auth-requirements [
    command_name: string  # Command to check (e.g., "server create", "cluster delete")
] {
    let metadata = (get-command-metadata $command_name)

    if ($metadata | type) == "record" {
        let requirements = ($metadata | get requirements? | default {})
        {
            requires_auth: ($requirements | get requires_auth? | default false)
            auth_type: ($requirements | get auth_type? | default "none")
            requires_confirmation: ($requirements | get requires_confirmation? | default false)
            min_permission: ($requirements | get min_permission? | default "read")
            side_effect_type: ($requirements | get side_effect_type? | default "none")
        }
    } else {
        {
            requires_auth: false
            auth_type: "none"
            requires_confirmation: false
            min_permission: "read"
            side_effect_type: "none"
        }
    }
}

# Determine if MFA is required based on metadata auth_type
def requires-mfa-from-metadata [
    command_name: string  # Command to check
] {
    let auth_reqs = (get-metadata-auth-requirements $command_name)
    $auth_reqs.auth_type == "mfa" or $auth_reqs.auth_type == "cedar"
}

# Determine if operation is destructive based on metadata
def is-destructive-from-metadata [
    command_name: string  # Command to check
] {
    let auth_reqs = (get-metadata-auth-requirements $command_name)
    $auth_reqs.side_effect_type == "delete"
}

# Check if metadata indicates this is a production operation
def is-production-from-metadata [
    command_name: string  # Command to check
] {
    let metadata = (get-command-metadata $command_name)

    if ($metadata | type) == "record" {
        let tags = ($metadata | get tags? | default [])
        ($tags | any { |tag| $tag == "production" or $tag == "deploy" })
    } else {
        false
    }
}

# Validate minimum permission level required by metadata
def validate-permission-level [
    command_name: string  # Command to check
    user_level: string    # User's permission level (read, write, admin, superadmin)
] {
    let auth_reqs = (get-metadata-auth-requirements $command_name)
    let required_level = $auth_reqs.min_permission

    # Permission level hierarchy (lower index = lower permission)
    let level_map = {
        read: 0
        write: 1
        admin: 2
        superadmin: 3
    }

    # Get required permission level index
    let req_level = (
        if $required_level == "read" { 0 }
        else if $required_level == "write" { 1 }
        else if $required_level == "admin" { 2 }
        else if $required_level == "superadmin" { 3 }
        else { -1 }
    )

    # Get user permission level index
    let usr_level = (
        if $user_level == "read" { 0 }
        else if $user_level == "write" { 1 }
        else if $user_level == "admin" { 2 }
        else if $user_level == "superadmin" { 3 }
        else { -1 }
    )

    # User must have equal or higher permission level
    if $req_level < 0 or $usr_level < 0 {
        return false
    }

    $usr_level >= $req_level
}

# Determine auth enforcement based on metadata
export def should-enforce-auth-from-metadata [
    command_name: string  # Command to check
] {
    # Get metadata for command and check auth requirements
    let metadata = (get-command-metadata $command_name)
    if ($metadata | type) == "record" {
        $metadata | get requirements.requires_auth? | default false
    } else {
        false
    }
}
