# Module: Authentication Implementation Details
# Purpose: Internal auth functions for policy enforcement, metadata evaluation, and auth flows
# Dependencies: config/accessor, plugins/kms, commands/traits, auth_core

# Selective imports (ADR-025 Phase 3 Layer 2).
use lib_provisioning/config/accessor/core.nu [config-get]
use lib_provisioning/commands/traits.nu [get-command-metadata]
use lib_provisioning/plugins/auth_core.nu [plugin-login plugin-mfa-enroll plugin-verify]

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

# Get auth requirements from metadata for a specific command
def get-metadata-auth-requirements [
    command_name: string
] {
    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
] {
    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
] {
    let auth_reqs = (get-metadata-auth-requirements $command_name)
    $auth_reqs.side_effect_type == "delete"
}

# Determine if operation is production-related
def is-production-from-metadata [
    command_name: string
] {
    let auth_reqs = (get-metadata-auth-requirements $command_name)
    $auth_reqs.auth_type == "mfa" or $auth_reqs.side_effect_type in ["delete" "modify"]
}

# Validate user has required permission level for operation
def validate-permission-level [
    operation_name: string
    user_level: string
] {
    let auth_reqs = (get-metadata-auth-requirements $operation_name)
    let min_perm = $auth_reqs.min_permission

    # Permission level hierarchy
    let req_level = (
        if $min_perm == "read" { 0 }
        else if $min_perm == "write" { 1 }
        else if $min_perm == "admin" { 2 }
        else if $min_perm == "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
] {
    let auth_reqs = (get-metadata-auth-requirements $command_name)

    # If metadata explicitly requires auth, enforce it
    if $auth_reqs.requires_auth {
        return true
    }

    # If side effects, enforce auth
    if $auth_reqs.side_effect_type != "none" {
        return true
    }

    # Otherwise check configuration
    (should-require-auth)
}

# ============================================================================
# Security Policy Enforcement Functions
# ============================================================================

# Check if authentication is required based on configuration
export def should-require-auth [] {
    let config_required = (config-get "security.require_auth" false)
    let env_bypass = ($env.PROVISIONING_SKIP_AUTH? | default "false") == "true"
    let allow_bypass = (config-get "security.bypass.allow_skip_auth" false)

    $config_required and not ($env_bypass and $allow_bypass)
}

# Check if MFA is required for production operations
export def should-require-mfa-prod [] {
    let environment = (config-get "environment" "dev")
    let require_mfa = (config-get "security.require_mfa_for_production" true)

    ($environment == "prod") and $require_mfa
}

# Check if MFA is required for destructive operations
export def should-require-mfa-destructive [] {
    (config-get "security.require_mfa_for_destructive" true)
}

# Check if user is authenticated
export def is-authenticated [] {
    let result = (plugin-verify)
    ($result | get valid? | default false)
}

# Check if MFA is verified
export def is-mfa-verified [] {
    let result = (plugin-verify)
    ($result | get mfa_verified? | default false)
}

# Get current authenticated user
export def get-authenticated-user [] {
    let result = (plugin-verify)
    ($result | get username? | default "")
}

# Require authentication with clear error messages
export def require-auth [
    operation: string
    --allow-skip
] {
    # Guard: Check if environment is dev (skip auth)
    let environment = (config-get "environment" "dev")
    if $environment == "dev" {
        # Auth not required in dev environment
        return true
    }

    if not (should-require-auth) {
        return true
    }

    if $allow_skip and (($env.PROVISIONING_SKIP_AUTH? | default "false") == "true") {
        print $"⚠️  Authentication bypassed with PROVISIONING_SKIP_AUTH flag"
        return true
    }

    let auth_status = (plugin-verify)

    if not ($auth_status | get valid? | default false) {
        print $"❌ Authentication Required"
        print $"Operation: ($operation)"
        exit 1
    }

    let username = ($auth_status | get username? | default "unknown")
    print $"✓ Authenticated as: ($username)"
    true
}

# Require MFA verification
export def require-mfa [
    operation: string
    reason: string
] {
    let auth_status = (plugin-verify)

    if not ($auth_status | get mfa_verified? | default false) {
        print $"❌ MFA Verification Required"
        print $"Operation: ($operation)"
        print $"Reason: ($reason)"
        exit 1
    }

    print $"✓ MFA verified"
    true
}

# Check auth for production operations
export def check-auth-for-production [
    operation: string
    --allow-skip
] {
    if (is-production-from-metadata $operation) {
        require-auth $operation --allow-skip=$allow_skip

        let requires_mfa_metadata = (requires-mfa-from-metadata $operation)
        if $requires_mfa_metadata or (should-require-mfa-prod) {
            require-mfa $operation "production environment operation"
        }

        return true
    }

    if (should-require-mfa-prod) {
        require-auth $operation --allow-skip=$allow_skip
        require-mfa $operation "production environment operation"
    }

    true
}

# Check auth for destructive operations
export def check-auth-for-destructive [
    operation: string
    --allow-skip
] {
    if (is-destructive-from-metadata $operation) {
        require-auth $operation --allow-skip=$allow_skip

        let requires_mfa_metadata = (requires-mfa-from-metadata $operation)
        if $requires_mfa_metadata or (should-require-mfa-destructive) {
            require-mfa $operation "destructive operation (delete/destroy)"
        }

        return true
    }

    if (should-require-mfa-destructive) {
        require-auth $operation --allow-skip=$allow_skip
        require-mfa $operation "destructive operation (delete/destroy)"
    }

    true
}

# Helper: Check if operation is in check mode
export def is-check-mode [flags: record] {
    (($flags | get check? | default false) or
     ($flags | get check_mode? | default false) or
     ($flags | get c? | default false))
}

# Helper: Determine if operation is destructive
export def is-destructive-operation [operation_type: string] {
    $operation_type in ["delete" "destroy" "remove"]
}

# Main authentication check for any operation
export def check-operation-auth [
    operation_name: string
    operation_type: string
    flags?: record
] {
    if ($flags | is-not-empty) and (is-check-mode $flags) {
        return true
    }

    if (should-enforce-auth-from-metadata $operation_name) {
        let auth_reqs = (get-metadata-auth-requirements $operation_name)

        let allow_skip = (config-get "security.bypass.allow_skip_auth" false)
        require-auth $operation_name --allow-skip=$allow_skip

        if $auth_reqs.auth_type == "mfa" {
            require-mfa $operation_name $"MFA required for ($operation_name)"
        }

        let user_level = (config-get "security.user_permission_level" "read")
        if not (validate-permission-level $operation_name $user_level) {
            print $"❌ Insufficient Permissions"
            exit 1
        }

        return true
    }

    if not (should-require-auth) {
        return true
    }

    let allow_skip = (config-get "security.bypass.allow_skip_auth" false)
    require-auth $operation_name --allow-skip=$allow_skip

    let environment = (config-get "environment" "dev")

    if $environment == "prod" and (should-require-mfa-prod) {
        require-mfa $operation_name "production environment"
    } else if (is-destructive-operation $operation_type) and (should-require-mfa-destructive) {
        require-mfa $operation_name "destructive operation"
    }

    true
}

# Get authentication metadata for audit logging
export def get-auth-metadata [] {
    let auth_status = (plugin-verify)

    {
        authenticated: ($auth_status | get valid? | default false)
        mfa_verified: ($auth_status | get mfa_verified? | default false)
        username: ($auth_status | get username? | default "anonymous")
        timestamp: (date now | format date "%Y-%m-%d %H:%M:%S")
    }
}

# Log authenticated operation for audit trail
export def log-authenticated-operation [
    operation: string
    details: record
] {
    let auth_metadata = (get-auth-metadata)

    let log_entry = {
        timestamp: $auth_metadata.timestamp
        user: $auth_metadata.username
        operation: $operation
        details: $details
        mfa_verified: $auth_metadata.mfa_verified
    }

    let log_path = (config-get "security.audit_log_path" "")
    if ($log_path | is-not-empty) {
        let log_dir = ($log_path | path dirname)
        if ($log_dir | path exists) {
            $log_entry | to json | save --append $log_path
        }
    }
}

# Print current authentication status
export def print-auth-status [] {
    let auth_status = (plugin-verify)
    let is_valid = ($auth_status | get valid? | default false)

    print $"Authentication Status"
    print $"━━━━━━━━━━━━━━━━━━━━"

    if $is_valid {
        let username = ($auth_status | get username? | default "unknown")
        let mfa_verified = ($auth_status | get mfa_verified? | default false)

        print $"Status: ✓ Authenticated"
        print $"User: ($username)"

        if $mfa_verified {
            print $"MFA: ✓ Verified"
        } else {
            print $"MFA: Not verified"
        }
    } else {
        print $"Status: ✗ Not authenticated"
        print ""
        print $"Run: provisioning auth login <username>"
    }

    print ""
    print $"Auth required: (should-require-auth)"
    print $"MFA for production: (should-require-mfa-prod)"
    print $"MFA for destructive: (should-require-mfa-destructive)"
}

# ============================================================================
# TYPEDIALOG HELPER FUNCTIONS
# ============================================================================

use lib_provisioning/utils/path-utils.nu [get-typedialog-form-path]

# Run TypeDialog form and return parsed result
export def run-typedialog-auth-form [
    form_path: string
    --backend: string = "tui"
] {
    if (which typedialog | is-empty) {
        return {
            success: false
            error: "TypeDialog plugin not available"
            use_fallback: true
        }
    }

    if not ($form_path | path exists) {
        return {
            success: false
            error: $"Form not found: ($form_path)"
            use_fallback: true
        }
    }

    let result = (typedialog form $form_path --backend $backend)

    if ($result | is-empty) {
        return {
            success: false
            error: "Form cancelled by user"
            use_fallback: false
        }
    }

    {
        success: true
        values: $result
        use_fallback: false
    }
}

# ============================================================================
# INTERACTIVE FORM HANDLERS (TypeDialog Integration)
# ============================================================================

# Interactive login with TypeDialog form
export def login-interactive [
    --backend: string = "tui"
] : nothing -> record {
    print "🔐 Interactive Authentication"
    print ""

    let form_path = (get-typedialog-form-path "auth-login.toml")
    let form_result = (run-typedialog-auth-form $form_path --backend $backend)

    if not $form_result.success or $form_result.use_fallback {
        print "ℹ️  TypeDialog not available. Using basic prompts..."
        print ""

        print "Username: "
        let username = (input)
        print "Password: "
        let password = (input --suppress-output)

        print "Do you have MFA enabled? (y/n): "
        let has_mfa_input = (input)
        let has_mfa = ($has_mfa_input == "y" or $has_mfa_input == "Y")

        let mfa_code = if $has_mfa {
            print "MFA Code (6 digits): "
            input
        } else {
            ""
        }

        if ($username | is-empty) or ($password | is-empty) {
            return {
                success: false
                error: "Username and password are required"
            }
        }

        let login_result = (plugin-login $username $password --mfa-code $mfa_code)

        return {
            success: true
            result: $login_result
            username: $username
            mfa_enabled: $has_mfa
        }
    }

    let form_values = $form_result.values

    if not ($form_values.auth?.confirm_login? | default false) {
        return {
            success: false
            error: "Login cancelled by user"
        }
    }

    let username = ($form_values.auth?.username? | default "")
    let password = ($form_values.auth?.password? | default "")
    let has_mfa = ($form_values.auth?.has_mfa? | default false)
    let mfa_code = if $has_mfa {
        $form_values.auth?.mfa_code? | default ""
    } else {
        ""
    }

    if ($username | is-empty) or ($password | is-empty) {
        return {
            success: false
            error: "Username and password are required"
        }
    }

    let login_result = (plugin-login $username $password --mfa-code $mfa_code)

    {
        success: true
        result: $login_result
        username: $username
        mfa_enabled: $has_mfa
    }
}

# Interactive MFA enrollment with TypeDialog form
export def mfa-enroll-interactive [
    --backend: string = "tui"
] : nothing -> record {
    print "🔐 Multi-Factor Authentication Setup"
    print ""

    let auth_status = (plugin-verify)
    let is_authenticated = ($auth_status.valid // false)

    if not $is_authenticated {
        return {
            success: false
            error: "Must be authenticated to enroll in MFA. Please login first."
        }
    }

    let form_path = (get-typedialog-form-path "mfa-enroll.toml")
    let form_result = (run-typedialog-auth-form $form_path --backend $backend)

    if not $form_result.success or $form_result.use_fallback {
        print "ℹ️  TypeDialog not available. Using basic prompts..."
        print ""

        print "MFA Type (totp/webauthn/sms): "
        let mfa_type = (input)

        let device_name = if ($mfa_type == "totp" or $mfa_type == "webauthn") {
            print "Device name: "
            input
        } else {
            ""
        }

        let phone_number = if $mfa_type == "sms" {
            print "Phone number: "
            input
        } else {
            ""
        }

        let verification_code = if ($mfa_type == "totp" or $mfa_type == "sms") {
            print "Verification code: "
            input
        } else {
            ""
        }

        return {
            success: true
            mfa_type: $mfa_type
            device_name: $device_name
            phone_number: $phone_number
            verification_code: $verification_code
        }
    }

    let form_values = $form_result.values

    if not ($form_values.mfa?.confirm_enroll? | default false) {
        return {
            success: false
            error: "MFA enrollment cancelled by user"
        }
    }

    let mfa_type = ($form_values.mfa?.type? | default "totp")
    let device_name = if $mfa_type == "totp" {
        $form_values.mfa?.totp?.device_name? | default "Authenticator App"
    } else if $mfa_type == "webauthn" {
        $form_values.mfa?.webauthn?.device_name? | default "Security Key"
    } else {
        ""
    }

    let phone_number = if $mfa_type == "sms" {
        $form_values.mfa?.sms?.phone_number? | default ""
    } else {
        ""
    }

    let verification_code = if $mfa_type == "totp" {
        $form_values.mfa?.totp?.verification_code? | default ""
    } else if $mfa_type == "sms" {
        $form_values.mfa?.sms?.verification_code? | default ""
    } else {
        ""
    }

    let generate_backup = ($form_values.mfa?.generate_backup_codes? | default true)
    let backup_count = ($form_values.mfa?.backup_codes_count? | default 10)

    let enroll_result = (plugin-mfa-enroll --type $mfa_type)

    {
        success: true
        result: $enroll_result
        mfa_type: $mfa_type
        device_name: $device_name
        phone_number: $phone_number
        verification_code: $verification_code
        generate_backup_codes: $generate_backup
        backup_codes_count: $backup_count
    }
}

# ============================================================================
# SIMPLE INPUT PROMPTS (for pipe and continue flows)
# ============================================================================

# Get API key from user input - outputs to stdout for piping
export def get-api-key-interactive [] : nothing -> string {
    print -n "Enter API Key: "
    let api_key = (input --suppress-output)

    if ($api_key | is-empty) {
        print "Error: API key cannot be empty" | error
        return ""
    }

    $api_key
}

# Get provider credentials - outputs JSON for continue flow
export def get-provider-credentials-interactive [] : nothing -> record {
    print -n "Enter username: "
    let username = (input)

    print -n "Enter password: "
    let password = (input --suppress-output)
    print ""

    if ($username | is-empty) or ($password | is-empty) {
        print "Error: Username and password cannot be empty" | error
        return {}
    }

    {
        username: $username
        password: $password
        timestamp: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
    }
}

# Get secret configuration input - outputs JSON for continue flow
export def get-secret-config-interactive [] : nothing -> record {
    print ""
    print "═══════════════════════════════════════════════════════════════"
    print "Secret Configuration"
    print "═══════════════════════════════════════════════════════════════"
    print ""

    print "Choose secret backend:"
    print "  1) SOPS (age/gpg encryption)"
    print "  2) HashiCorp Vault"
    print "  3) AWS Secrets Manager"
    print ""
    print -n "Select backend (1-3): "
    let backend_choice = (input)

    let backend = match $backend_choice {
        "1" => "sops"
        "2" => "vault"
        "3" => "aws-secrets"
        _ => "sops"
    }

    print ""
    print -n "Enter secret location/path: "
    let secret_path = (input)

    if ($secret_path | is-empty) {
        print "Error: Secret path cannot be empty" | error
        return {}
    }

    {
        backend: $backend
        secret_path: $secret_path
        timestamp: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
    }
}
