#!/usr/bin/env nu

# Configure access policies for OCI registry namespaces

export def main [
    --registry-url: string = "localhost:5000"
    --registry-type: string = "zot"
    --admin-password: string = "admin"
] {
    print "🔒 Configuring OCI registry access policies..."
    print $"   Registry: ($registry_url)"
    print $"   Type: ($registry_type)\n"

    let policies = get-policy-definitions

    match $registry_type {
        "zot" => {
            configure-zot-policies $registry_url $policies
        }
        "harbor" => {
            configure-harbor-policies $registry_url $policies $admin_password
        }
        "distribution" => {
            configure-distribution-policies $registry_url $policies
        }
        _ => {
            print $"⚠ Unknown registry type: ($registry_type)"
        }
    }

    print "\n✅ Policy configuration complete!"
}

def get-policy-definitions [] -> list {
    [
        {
            namespace: "provisioning-extensions"
            access: {
                authenticated: {
                    read: true
                    write: true
                    delete: true
                }
                anonymous: {
                    read: false
                    write: false
                    delete: false
                }
            }
            users: [
                {name: "provisioning", role: "admin"}
                {name: "developer", role: "developer"}
            ]
            webhooks: [
                {
                    event: "push"
                    url: "http://orchestrator:8080/registry/events/push"
                }
                {
                    event: "delete"
                    url: "http://orchestrator:8080/registry/events/delete"
                }
            ]
        }
        {
            namespace: "provisioning-kcl"
            access: {
                authenticated: {
                    read: true
                    write: true
                    delete: false
                }
                anonymous: {
                    read: false
                    write: false
                    delete: false
                }
            }
            users: [
                {name: "provisioning", role: "admin"}
                {name: "developer", role: "developer"}
            ]
            webhooks: [
                {
                    event: "push"
                    url: "http://orchestrator:8080/registry/events/push"
                }
            ]
        }
        {
            namespace: "provisioning-platform"
            access: {
                authenticated: {
                    read: true
                    write: false
                    delete: false
                }
                anonymous: {
                    read: false
                    write: false
                    delete: false
                }
            }
            users: [
                {name: "provisioning", role: "admin"}
            ]
            webhooks: [
                {
                    event: "push"
                    url: "http://orchestrator:8080/registry/events/push"
                }
            ]
        }
        {
            namespace: "provisioning-test"
            access: {
                authenticated: {
                    read: true
                    write: true
                    delete: true
                }
                anonymous: {
                    read: true
                    write: false
                    delete: false
                }
            }
            users: [
                {name: "provisioning", role: "admin"}
                {name: "developer", role: "developer"}
                {name: "tester", role: "developer"}
            ]
            webhooks: []
        }
    ]
}

def configure-zot-policies [registry_url: string, policies: list] {
    print "Configuring Zot policies..."
    print "\nℹ Zot policies are configured in config.json"
    print "  The following policies are defined:\n"

    for policy in $policies {
        print $"📦 ($policy.namespace):"
        print $"   Authenticated users:"
        print $"     Read: ($policy.access.authenticated.read)"
        print $"     Write: ($policy.access.authenticated.write)"
        print $"     Delete: ($policy.access.authenticated.delete)"
        print $"   Anonymous users:"
        print $"     Read: ($policy.access.anonymous.read)"
        print $"   Users: ($policy.users | get name | str join ', ')"
        if ($policy.webhooks | length) > 0 {
            print $"   Webhooks: ($policy.webhooks | length) configured"
        }
        print ""
    }

    print "ℹ To update policies, edit zot/config.json and restart the registry"
}

def configure-harbor-policies [
    registry_url: string
    policies: list
    admin_password: string
] {
    print "Configuring Harbor policies via API...\n"

    let auth = $"admin:($admin_password)" | encode base64

    for policy in $policies {
        print $"📦 Configuring ($policy.namespace)..."

        # Get project ID
        let project_id = get-harbor-project-id $registry_url $policy.namespace $auth

        if ($project_id | is-empty) {
            print $"  ⚠ Project not found, skipping"
            continue
        }

        # Configure RBAC
        configure-harbor-rbac $registry_url $project_id $policy.users $auth

        # Configure webhooks
        if ($policy.webhooks | length) > 0 {
            configure-harbor-webhooks $registry_url $project_id $policy.webhooks $auth
        }

        print $"  ✓ Policies configured"
        print ""
    }
}

def get-harbor-project-id [
    registry_url: string
    project_name: string
    auth: string
] -> int {
    let result = (do {
        http get $"http://($registry_url)/api/v2.0/projects?name=($project_name)" --headers {
            "Authorization": $"Basic ($auth)"
        }
    } | complete)

    if $result.exit_code == 0 {
        let projects = ($result.stdout | from json)
        if ($projects | length) > 0 {
            return ($projects | first | get project_id)
        }
    }

    return null
}

def configure-harbor-rbac [
    registry_url: string
    project_id: int
    users: list
    auth: string
] {
    print "  Configuring RBAC..."

    for user in $users {
        let role_id = match $user.role {
            "admin" => 1
            "developer" => 2
            "guest" => 3
            _ => 2
        }

        let member_body = {
            role_id: $role_id
            member_user: {
                username: $user.name
            }
        }

        let result = (do {
            http post $"http://($registry_url)/api/v2.0/projects/($project_id)/members" $member_body --headers {
                "Authorization": $"Basic ($auth)"
                "Content-Type": "application/json"
            }
        } | complete)

        if $result.exit_code == 0 {
            print $"    ✓ User ($user.name) added as ($user.role)"
        } else {
            if ($result.stderr | str contains "already exists") {
                print $"    ℹ User ($user.name) already exists"
            } else {
                print $"    ⚠ Error adding user ($user.name)"
            }
        }
    }
}

def configure-harbor-webhooks [
    registry_url: string
    project_id: int
    webhooks: list
    auth: string
] {
    print "  Configuring webhooks..."

    for webhook in $webhooks {
        let webhook_body = {
            name: $"webhook-($webhook.event)"
            description: $"Webhook for ($webhook.event) events"
            enabled: true
            event_types: [$webhook.event]
            targets: [
                {
                    type: "http"
                    address: $webhook.url
                    skip_cert_verify: true
                }
            ]
        }

        let result = (do {
            http post $"http://($registry_url)/api/v2.0/projects/($project_id)/webhook/policies" $webhook_body --headers {
                "Authorization": $"Basic ($auth)"
                "Content-Type": "application/json"
            }
        } | complete)

        if $result.exit_code == 0 {
            print $"    ✓ Webhook for ($webhook.event) configured"
        } else {
            print $"    ⚠ Error configuring webhook for ($webhook.event)"
        }
    }
}

def configure-distribution-policies [registry_url: string, policies: list] {
    print "Configuring Distribution policies...\n"
    print "ℹ Distribution uses htpasswd for authentication"
    print "  Generating htpasswd file...\n"

    # Collect all unique users
    let all_users = ($policies | each { |p| $p.users } | flatten | get name | uniq)

    print "Users to create:"
    for user in $all_users {
        print $"  - ($user)"
    }

    print "\nℹ To create users, run:"
    print "  htpasswd -Bc htpasswd <username>"
    print "\nℹ Access control is enforced at the authentication layer"
    print "  All authenticated users can push/pull"
    print "  Anonymous users can only pull if allowed in config.yml"
}

# Helper: Show policy for namespace
export def "policy show" [
    namespace: string
] {
    let policies = get-policy-definitions
    let policy = ($policies | where namespace == $namespace | first)

    if ($policy | is-empty) {
        print $"Policy for namespace ($namespace) not found"
        return
    }

    print $"Policy for: ($policy.namespace)\n"

    print "Access Control:"
    print "  Authenticated users:"
    print $"    Read: ($policy.access.authenticated.read)"
    print $"    Write: ($policy.access.authenticated.write)"
    print $"    Delete: ($policy.access.authenticated.delete)"
    print "  Anonymous users:"
    print $"    Read: ($policy.access.anonymous.read)"
    print $"    Write: ($policy.access.anonymous.write)"
    print $"    Delete: ($policy.access.anonymous.delete)"

    print "\nUsers:"
    for user in $policy.users {
        print $"  - ($user.name) (($user.role))"
    }

    if ($policy.webhooks | length) > 0 {
        print "\nWebhooks:"
        for webhook in $policy.webhooks {
            print $"  - Event: ($webhook.event)"
            print $"    URL: ($webhook.url)"
        }
    }
}

# Helper: List all policies
export def "policy list" [] -> table {
    let policies = get-policy-definitions

    $policies | each { |p|
        {
            namespace: $p.namespace
            auth_read: $p.access.authenticated.read
            auth_write: $p.access.authenticated.write
            anon_read: $p.access.anonymous.read
            users: ($p.users | length)
            webhooks: ($p.webhooks | length)
        }
    }
}
