package iam

import (
	"time"

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

// Group represents a user group
type Group struct {
	ID          string
	Name        string
	Description string
	IsSystem    bool
	CreatedAt   time.Time
	UpdatedAt   time.Time
	UserCount   int
	RoleCount   int
}

// GetGroupByID retrieves a group by ID
func GetGroupByID(db *database.DB, groupID string) (*Group, error) {
	query := `
		SELECT id, name, description, is_system, created_at, updated_at
		FROM groups
		WHERE id = $1
	`

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

	// Get user count
	var userCount int
	db.QueryRow("SELECT COUNT(*) FROM user_groups WHERE group_id = $1", groupID).Scan(&userCount)
	group.UserCount = userCount

	// Get role count
	var roleCount int
	db.QueryRow("SELECT COUNT(*) FROM group_roles WHERE group_id = $1", groupID).Scan(&roleCount)
	group.RoleCount = roleCount

	return &group, nil
}

// GetGroupByName retrieves a group by name
func GetGroupByName(db *database.DB, name string) (*Group, error) {
	query := `
		SELECT id, name, description, is_system, created_at, updated_at
		FROM groups
		WHERE name = $1
	`

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

	return &group, nil
}

// GetUserGroups retrieves all groups for a user
func GetUserGroups(db *database.DB, userID string) ([]string, error) {
	query := `
		SELECT g.name
		FROM groups g
		INNER JOIN user_groups ug ON g.id = ug.group_id
		WHERE ug.user_id = $1
		ORDER BY g.name
	`

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

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

	if groups == nil {
		groups = []string{}
	}
	return groups, rows.Err()
}

// GetGroupUsers retrieves all users in a group
func GetGroupUsers(db *database.DB, groupID string) ([]string, error) {
	query := `
		SELECT u.id
		FROM users u
		INNER JOIN user_groups ug ON u.id = ug.user_id
		WHERE ug.group_id = $1
		ORDER BY u.username
	`

	rows, err := db.Query(query, groupID)
	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()
}

// GetGroupRoles retrieves all roles for a group
func GetGroupRoles(db *database.DB, groupID string) ([]string, error) {
	query := `
		SELECT r.name
		FROM roles r
		INNER JOIN group_roles gr ON r.id = gr.role_id
		WHERE gr.group_id = $1
		ORDER BY r.name
	`

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

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

	return roles, rows.Err()
}

// AddUserToGroup adds a user to a group
func AddUserToGroup(db *database.DB, userID, groupID, assignedBy string) error {
	query := `
		INSERT INTO user_groups (user_id, group_id, assigned_by)
		VALUES ($1, $2, $3)
		ON CONFLICT (user_id, group_id) DO NOTHING
	`
	_, err := db.Exec(query, userID, groupID, assignedBy)
	return err
}

// RemoveUserFromGroup removes a user from a group
func RemoveUserFromGroup(db *database.DB, userID, groupID string) error {
	query := `DELETE FROM user_groups WHERE user_id = $1 AND group_id = $2`
	_, err := db.Exec(query, userID, groupID)
	return err
}

// AddRoleToGroup adds a role to a group
func AddRoleToGroup(db *database.DB, groupID, roleID string) error {
	query := `
		INSERT INTO group_roles (group_id, role_id)
		VALUES ($1, $2)
		ON CONFLICT (group_id, role_id) DO NOTHING
	`
	_, err := db.Exec(query, groupID, roleID)
	return err
}

// RemoveRoleFromGroup removes a role from a group
func RemoveRoleFromGroup(db *database.DB, groupID, roleID string) error {
	query := `DELETE FROM group_roles WHERE group_id = $1 AND role_id = $2`
	_, err := db.Exec(query, groupID, roleID)
	return err
}

// GetUserRolesFromGroups retrieves all roles for a user via groups
func GetUserRolesFromGroups(db *database.DB, userID string) ([]string, error) {
	query := `
		SELECT DISTINCT r.name
		FROM roles r
		INNER JOIN group_roles gr ON r.id = gr.role_id
		INNER JOIN user_groups ug ON gr.group_id = ug.group_id
		WHERE ug.user_id = $1
		ORDER BY r.name
	`

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

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

	return roles, rows.Err()
}
