// Package security provides the impure loader for security policy YAML files.
// It reads security/ directory files and returns a pure security.SecurityPolicy.
package security

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"gopkg.in/yaml.v3"

	"github.com/enmanuel/agents/pkg/security"
)

// --- YAML intermediate types (private, only for parsing) ---

type yamlUserGroups struct {
	Groups map[string]struct {
		Members []string `yaml:"members"`
	} `yaml:"groups"`
}

type yamlAgentGroups struct {
	Groups map[string]struct {
		Agents []string `yaml:"agents"`
	} `yaml:"groups"`
}

type yamlPermissions struct {
	Policies []struct {
		AgentGroup  string `yaml:"agent_group"`
		Permissions []struct {
			UserGroup string   `yaml:"user_group"`
			Actions   []string `yaml:"actions"`
		} `yaml:"permissions"`
	} `yaml:"policies"`
}

// Load reads the security YAML files from dir and returns a SecurityPolicy.
// If dir does not exist or is empty, returns an empty policy without error.
// If an individual file is missing, that section is left empty.
// If a YAML file is malformed, returns an error naming the file.
func Load(dir string) (security.SecurityPolicy, error) {
	if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) {
		return security.SecurityPolicy{}, nil
	}

	userGroups, err := loadUserGroups(filepath.Join(dir, "user-groups.yaml"))
	if err != nil {
		return security.SecurityPolicy{}, err
	}

	agentGroups, err := loadAgentGroups(filepath.Join(dir, "agent-groups.yaml"))
	if err != nil {
		return security.SecurityPolicy{}, err
	}

	policies, err := loadPermissions(filepath.Join(dir, "permissions.yaml"))
	if err != nil {
		return security.SecurityPolicy{}, err
	}

	return security.SecurityPolicy{
		UserGroups:  userGroups,
		AgentGroups: agentGroups,
		Policies:    policies,
	}, nil
}

func loadUserGroups(path string) ([]security.UserGroup, error) {
	data, err := os.ReadFile(path)
	if errors.Is(err, os.ErrNotExist) {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("security: reading %s: %w", path, err)
	}

	var raw yamlUserGroups
	if err := yaml.Unmarshal(data, &raw); err != nil {
		return nil, fmt.Errorf("security: parsing %s: %w", path, err)
	}

	groups := make([]security.UserGroup, 0, len(raw.Groups))
	for name, g := range raw.Groups {
		groups = append(groups, security.UserGroup{
			Name:    name,
			Members: g.Members,
		})
	}
	return groups, nil
}

func loadAgentGroups(path string) ([]security.AgentGroup, error) {
	data, err := os.ReadFile(path)
	if errors.Is(err, os.ErrNotExist) {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("security: reading %s: %w", path, err)
	}

	var raw yamlAgentGroups
	if err := yaml.Unmarshal(data, &raw); err != nil {
		return nil, fmt.Errorf("security: parsing %s: %w", path, err)
	}

	groups := make([]security.AgentGroup, 0, len(raw.Groups))
	for name, g := range raw.Groups {
		groups = append(groups, security.AgentGroup{
			Name:   name,
			Agents: g.Agents,
		})
	}
	return groups, nil
}

func loadPermissions(path string) ([]security.AgentPolicy, error) {
	data, err := os.ReadFile(path)
	if errors.Is(err, os.ErrNotExist) {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("security: reading %s: %w", path, err)
	}

	var raw yamlPermissions
	if err := yaml.Unmarshal(data, &raw); err != nil {
		return nil, fmt.Errorf("security: parsing %s: %w", path, err)
	}

	policies := make([]security.AgentPolicy, 0, len(raw.Policies))
	for _, p := range raw.Policies {
		perms := make([]security.Permission, 0, len(p.Permissions))
		for _, perm := range p.Permissions {
			perms = append(perms, security.Permission{
				UserGroup: perm.UserGroup,
				Actions:   perm.Actions,
			})
		}
		policies = append(policies, security.AgentPolicy{
			AgentGroup:  p.AgentGroup,
			Permissions: perms,
		})
	}
	return policies, nil
}
