package auth

import (
	"fmt"
	"sync"

	"github.com/jfraeys/fetch_ml/internal/fileutil"
	"gopkg.in/yaml.v3"
)

// PermissionConfig represents the permissions configuration
type PermissionConfig struct {
	Roles     map[string]RoleConfig      `yaml:"roles"`
	Groups    map[string]GroupConfig     `yaml:"groups"`
	Hierarchy map[string]HierarchyConfig `yaml:"hierarchy"`
	Defaults  DefaultsConfig             `yaml:"defaults"`
}

// RoleConfig defines a role and its permissions
type RoleConfig struct {
	Description string   `yaml:"description"`
	Permissions []string `yaml:"permissions"`
}

// GroupConfig defines a permission group
type GroupConfig struct {
	Description string   `yaml:"description"`
	Inherits    []string `yaml:"inherits"`
	Permissions []string `yaml:"permissions"`
}

// HierarchyConfig defines resource hierarchy
type HierarchyConfig struct {
	Children map[string]interface{} `yaml:"children"`
	Special  map[string]string      `yaml:"special"`
}

// DefaultsConfig defines default settings
type DefaultsConfig struct {
	NewUserRole string   `yaml:"new_user_role"`
	AdminUsers  []string `yaml:"admin_users"`
}

// PermissionManager manages permissions from YAML file
type PermissionManager struct {
	config     *PermissionConfig
	rolePerms  map[string]map[string]bool
	groupPerms map[string]map[string]bool
	mu         sync.RWMutex
	loaded     bool
}

// NewPermissionManager creates a new permission manager
func NewPermissionManager(configPath string) (*PermissionManager, error) {
	pm := &PermissionManager{}

	if err := pm.loadConfig(configPath); err != nil {
		return nil, fmt.Errorf("failed to load permissions: %w", err)
	}

	return pm, nil
}

// loadConfig loads permissions from YAML file
func (pm *PermissionManager) loadConfig(configPath string) error {
	pm.mu.Lock()
	defer pm.mu.Unlock()

	data, err := fileutil.SecureFileRead(configPath)
	if err != nil {
		return fmt.Errorf("failed to read permissions file: %w", err)
	}

	var config PermissionConfig
	if err := yaml.Unmarshal(data, &config); err != nil {
		return fmt.Errorf("failed to parse permissions file: %w", err)
	}

	pm.config = &config
	pm.rolePerms = make(map[string]map[string]bool)
	pm.groupPerms = make(map[string]map[string]bool)

	// Process role permissions
	for roleName, role := range config.Roles {
		perms := make(map[string]bool)
		for _, perm := range role.Permissions {
			perms[perm] = true
		}
		pm.rolePerms[roleName] = perms
	}

	// Process group permissions
	for groupName, group := range config.Groups {
		perms := make(map[string]bool)

		// Add direct permissions
		for _, perm := range group.Permissions {
			perms[perm] = true
		}

		// Inherit permissions from other roles/groups
		for _, inherit := range group.Inherits {
			if rolePerms, exists := pm.rolePerms[inherit]; exists {
				for perm, value := range rolePerms {
					perms[perm] = value
				}
			}
			if groupPerms, exists := pm.groupPerms[inherit]; exists {
				for perm, value := range groupPerms {
					perms[perm] = value
				}
			}
		}

		pm.groupPerms[groupName] = perms
	}

	pm.loaded = true
	return nil
}

// GetRolePermissions returns permissions for a role
func (pm *PermissionManager) GetRolePermissions(role string) map[string]bool {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	if !pm.loaded {
		return make(map[string]bool)
	}

	if perms, exists := pm.rolePerms[role]; exists {
		result := make(map[string]bool)
		for perm, value := range perms {
			result[perm] = value
		}
		return result
	}

	return make(map[string]bool)
}

// GetGroupPermissions returns permissions for a group
func (pm *PermissionManager) GetGroupPermissions(group string) map[string]bool {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	if !pm.loaded {
		return make(map[string]bool)
	}

	if perms, exists := pm.groupPerms[group]; exists {
		result := make(map[string]bool)
		for perm, value := range perms {
			result[perm] = value
		}
		return result
	}

	return make(map[string]bool)
}

// GetAllRoles returns all available roles
func (pm *PermissionManager) GetAllRoles() map[string]RoleConfig {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	if !pm.loaded {
		return make(map[string]RoleConfig)
	}

	result := make(map[string]RoleConfig)
	for name, role := range pm.config.Roles {
		result[name] = role
	}
	return result
}

// GetAllGroups returns all available groups
func (pm *PermissionManager) GetAllGroups() map[string]GroupConfig {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	if !pm.loaded {
		return make(map[string]GroupConfig)
	}

	result := make(map[string]GroupConfig)
	for name, group := range pm.config.Groups {
		result[name] = group
	}
	return result
}

// GetDefaultRole returns the default role for new users
func (pm *PermissionManager) GetDefaultRole() string {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	if !pm.loaded || pm.config.Defaults.NewUserRole == "" {
		return "viewer"
	}

	return pm.config.Defaults.NewUserRole
}

// IsAdminUser checks if a username should have admin rights
func (pm *PermissionManager) IsAdminUser(username string) bool {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	if !pm.loaded {
		return false
	}

	for _, adminUser := range pm.config.Defaults.AdminUsers {
		if adminUser == username {
			return true
		}
	}
	return false
}

// ReloadConfig reloads the permissions configuration
func (pm *PermissionManager) ReloadConfig(configPath string) error {
	return pm.loadConfig(configPath)
}

// ValidatePermission checks if a permission string is valid
func (pm *PermissionManager) ValidatePermission(permission string) bool {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	if !pm.loaded {
		return false
	}

	// Wildcard is always valid
	if permission == "*" {
		return true
	}

	// Check if permission matches any defined role permissions
	for _, rolePerms := range pm.rolePerms {
		if _, exists := rolePerms[permission]; exists {
			return true
		}
	}

	// Check if permission matches any group permissions
	for _, groupPerms := range pm.groupPerms {
		if _, exists := groupPerms[permission]; exists {
			return true
		}
	}

	return false
}

// GetPermissionHierarchy returns the hierarchy for a resource
func (pm *PermissionManager) GetPermissionHierarchy(resource string) map[string]interface{} {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	if !pm.loaded {
		return make(map[string]interface{})
	}

	if hierarchy, exists := pm.config.Hierarchy[resource]; exists {
		return hierarchy.Children
	}

	return make(map[string]interface{})
}

// Global permission manager instance
var globalPermissionManager *PermissionManager
var permissionManagerOnce sync.Once

// GetGlobalPermissionManager returns the global permission manager
func GetGlobalPermissionManager() *PermissionManager {
	permissionManagerOnce.Do(func() {
		// Try to load from default location
		if pm, err := NewPermissionManager("configs/schema/permissions.yaml"); err == nil {
			globalPermissionManager = pm
		} else {
			// Fallback to empty manager
			globalPermissionManager = &PermissionManager{
				rolePerms:  make(map[string]map[string]bool),
				groupPerms: make(map[string]map[string]bool),
				loaded:     false,
			}
		}
	})
	return globalPermissionManager
}
