package iam

import (
	"time"

	"github.com/atlasos/calypso/internal/common/database"
)

// Role represents a system role
type Role struct {
	ID          string
	Name        string
	Description string
	IsSystem    bool
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

// GetRoleByID retrieves a role by ID
func GetRoleByID(db *database.DB, roleID string) (*Role, error) {
	query := `
		SELECT id, name, description, is_system, created_at, updated_at
		FROM roles
		WHERE id = $1
	`

	var role Role
	err := db.QueryRow(query, roleID).Scan(
		&role.ID, &role.Name, &role.Description, &role.IsSystem,
		&role.CreatedAt, &role.UpdatedAt,
	)
	if err != nil {
		return nil, err
	}

	return &role, nil
}

// GetRoleByName retrieves a role by name
func GetRoleByName(db *database.DB, name string) (*Role, error) {
	query := `
		SELECT id, name, description, is_system, created_at, updated_at
		FROM roles
		WHERE name = $1
	`

	var role Role
	err := db.QueryRow(query, name).Scan(
		&role.ID, &role.Name, &role.Description, &role.IsSystem,
		&role.CreatedAt, &role.UpdatedAt,
	)
	if err != nil {
		return nil, err
	}

	return &role, nil
}

// ListRoles retrieves all roles
func ListRoles(db *database.DB) ([]*Role, error) {
	query := `
		SELECT id, name, description, is_system, created_at, updated_at
		FROM roles
		ORDER BY name
	`

	rows, err := db.Query(query)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var roles []*Role
	for rows.Next() {
		var role Role
		if err := rows.Scan(
			&role.ID, &role.Name, &role.Description, &role.IsSystem,
			&role.CreatedAt, &role.UpdatedAt,
		); err != nil {
			return nil, err
		}
		roles = append(roles, &role)
	}

	return roles, rows.Err()
}

// CreateRole creates a new role
func CreateRole(db *database.DB, name, description string) (*Role, error) {
	query := `
		INSERT INTO roles (name, description)
		VALUES ($1, $2)
		RETURNING id, name, description, is_system, created_at, updated_at
	`

	var role Role
	err := db.QueryRow(query, name, description).Scan(
		&role.ID, &role.Name, &role.Description, &role.IsSystem,
		&role.CreatedAt, &role.UpdatedAt,
	)
	if err != nil {
		return nil, err
	}

	return &role, nil
}

// UpdateRole updates an existing role
func UpdateRole(db *database.DB, roleID, name, description string) error {
	query := `
		UPDATE roles
		SET name = $1, description = $2, updated_at = NOW()
		WHERE id = $3
	`
	_, err := db.Exec(query, name, description, roleID)
	return err
}

// DeleteRole deletes a role
func DeleteRole(db *database.DB, roleID string) error {
	query := `DELETE FROM roles WHERE id = $1`
	_, err := db.Exec(query, roleID)
	return err
}

// GetRoleUsers retrieves all users with a specific role
func GetRoleUsers(db *database.DB, roleID string) ([]string, error) {
	query := `
		SELECT u.id
		FROM users u
		INNER JOIN user_roles ur ON u.id = ur.user_id
		WHERE ur.role_id = $1
		ORDER BY u.username
	`

	rows, err := db.Query(query, roleID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var userIDs []string
	for rows.Next() {
		var userID string
		if err := rows.Scan(&userID); err != nil {
			return nil, err
		}
		userIDs = append(userIDs, userID)
	}

	return userIDs, rows.Err()
}

// GetRolePermissions retrieves all permissions for a role
func GetRolePermissions(db *database.DB, roleID string) ([]string, error) {
	query := `
		SELECT p.name
		FROM permissions p
		INNER JOIN role_permissions rp ON p.id = rp.permission_id
		WHERE rp.role_id = $1
		ORDER BY p.name
	`

	rows, err := db.Query(query, roleID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var permissions []string
	for rows.Next() {
		var perm string
		if err := rows.Scan(&perm); err != nil {
			return nil, err
		}
		permissions = append(permissions, perm)
	}

	return permissions, rows.Err()
}

// AddPermissionToRole assigns a permission to a role
func AddPermissionToRole(db *database.DB, roleID, permissionID string) error {
	query := `
		INSERT INTO role_permissions (role_id, permission_id)
		VALUES ($1, $2)
		ON CONFLICT (role_id, permission_id) DO NOTHING
	`
	_, err := db.Exec(query, roleID, permissionID)
	return err
}

// RemovePermissionFromRole removes a permission from a role
func RemovePermissionFromRole(db *database.DB, roleID, permissionID string) error {
	query := `DELETE FROM role_permissions WHERE role_id = $1 AND permission_id = $2`
	_, err := db.Exec(query, roleID, permissionID)
	return err
}

// GetPermissionIDByName retrieves a permission ID by name
func GetPermissionIDByName(db *database.DB, permissionName string) (string, error) {
	var permissionID string
	err := db.QueryRow("SELECT id FROM permissions WHERE name = $1", permissionName).Scan(&permissionID)
	return permissionID, err
}

// ListPermissions retrieves all permissions
func ListPermissions(db *database.DB) ([]map[string]interface{}, error) {
	query := `
		SELECT id, name, resource, action, description
		FROM permissions
		ORDER BY resource, action
	`

	rows, err := db.Query(query)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var permissions []map[string]interface{}
	for rows.Next() {
		var id, name, resource, action, description string
		if err := rows.Scan(&id, &name, &resource, &action, &description); err != nil {
			return nil, err
		}
		permissions = append(permissions, map[string]interface{}{
			"id":          id,
			"name":        name,
			"resource":    resource,
			"action":      action,
			"description": description,
		})
	}

	return permissions, rows.Err()
}
