package auth

import (
	"context"
	"encoding/json"
	"log"

	"switchboard-core/database"
	"switchboard-core/store"
)

// EveryoneGroupID is the stable ID of the implicit "Everyone" group seeded in
// migration 002. Every authenticated user receives its permissions without an
// explicit membership row.
const EveryoneGroupID = "00000000-0000-0000-0000-000000000001"

// AdminsGroupID is the stable ID of the "Admins" system group seeded in
// migration 002. Members receive surface.admin.access and all platform
// permissions. Replaces the legacy users.role = 'admin' check.
const AdminsGroupID = "00000000-0000-0000-0000-000000000002"

// Permission constants — domain.action convention.
const (
	PermSurfaceAdminAccess = "surface.admin.access" // full admin panel access (replaces role check)
	PermExtensionUse       = "extension.use"        // use installed extensions
	PermExtensionInstall   = "extension.install"     // install/manage extension packages
	PermWorkflowCreate     = "workflow.create"       // create workflow definitions
	PermWorkflowSubmit     = "workflow.submit"       // submit to public workflows
	PermAdminView          = "admin.view"            // read-only admin panel access
	PermTokenUnlimited     = "token.unlimited"       // bypass token budgets
)

// AllPermissions is the complete set of valid permission strings.
// Used for validation in handlers and rendering checkboxes in admin UI.
var AllPermissions = []string{
	PermSurfaceAdminAccess,
	PermExtensionUse,
	PermExtensionInstall,
	PermWorkflowCreate,
	PermWorkflowSubmit,
	PermAdminView,
	PermTokenUnlimited,
}

// ── Resolution ──────────────────────────────

// ResolvePermissions returns the effective permission set for a user.
// Unions permissions from all groups the user is a member of (including Everyone).
func ResolvePermissions(ctx context.Context, stores store.Stores, userID string) (map[string]bool, error) {
	perms := make(map[string]bool)

	groups, err := stores.Groups.ListForUser(ctx, userID)
	if err != nil {
		return perms, err
	}
	for _, g := range groups {
		for _, p := range g.Permissions {
			perms[p] = true
		}
	}

	return perms, nil
}

// EnsureEveryoneGroup adds a user to the Everyone group (idempotent).
// Called on every user creation path so that Everyone membership is explicit.
func EnsureEveryoneGroup(ctx context.Context, stores store.Stores, userID string) {
	_ = stores.Groups.AddMember(ctx, EveryoneGroupID, userID, userID)
}

// EnsureAdminsGroup creates the Admins system group if it does not exist.
// Handles the case where migration 002 was applied before the Admins INSERT
// was added (no new migrations pre-MVP — edits in place).
// Uses raw SQL because the store's Create() overwrites the ID.
func EnsureAdminsGroup(ctx context.Context, stores store.Stores) {
	if _, err := stores.Groups.GetByID(ctx, AdminsGroupID); err == nil {
		return // already exists
	}
	permsJSON, _ := json.Marshal(AllPermissions)
	db := database.DB
	if db == nil {
		return
	}
	_, err := db.ExecContext(ctx, `
		INSERT OR IGNORE INTO groups (id, name, description, scope, created_by, source, permissions)
		VALUES (?, 'Admins', 'Full platform access — replaces legacy admin role.',
		        'global', NULL, 'system', ?)`,
		AdminsGroupID, string(permsJSON))
	if err != nil {
		// Postgres variant
		_, err = db.ExecContext(ctx, `
			INSERT INTO groups (id, name, description, scope, created_by, source, permissions)
			VALUES ($1, 'Admins', 'Full platform access — replaces legacy admin role.',
			        'global', NULL, 'system', $2::jsonb)
			ON CONFLICT (id) DO NOTHING`,
			AdminsGroupID, string(permsJSON))
		if err != nil {
			log.Printf("⚠ EnsureAdminsGroup: %v", err)
		}
	}
}

// AddToAdminsGroup adds a user to the Admins group (idempotent).
// Ensures the Admins group exists before attempting membership.
func AddToAdminsGroup(ctx context.Context, stores store.Stores, userID string) {
	EnsureAdminsGroup(ctx, stores)
	_ = stores.Groups.AddMember(ctx, AdminsGroupID, userID, userID)
}

// RemoveFromAdminsGroup removes a user from the Admins group.
func RemoveFromAdminsGroup(ctx context.Context, stores store.Stores, userID string) {
	_ = stores.Groups.RemoveMember(ctx, AdminsGroupID, userID)
}

