package auth

import (
	"context"
	"database/sql"
)

type Permission struct {
	ID          string
	Name        string
	Description string
}

type Role struct {
	ID          string
	Name        string
	Description string
}

type RBACStore struct {
	DB *sql.DB
}

func NewRBACStore(db *sql.DB) *RBACStore {
	return &RBACStore{DB: db}
}

// GetUserRoles retrieves all roles for a user
func (s *RBACStore) GetUserRoles(ctx context.Context, userID string) ([]Role, error) {
	rows, err := s.DB.QueryContext(ctx,
		`SELECT r.id, r.name, r.description FROM roles r
		 INNER JOIN user_roles ur ON r.id = ur.role_id
		 WHERE ur.user_id = ?`,
		userID)
	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); err != nil {
			return nil, err
		}
		roles = append(roles, role)
	}
	return roles, rows.Err()
}

// GetRolePermissions retrieves all permissions for a role
func (s *RBACStore) GetRolePermissions(ctx context.Context, roleID string) ([]Permission, error) {
	rows, err := s.DB.QueryContext(ctx,
		`SELECT p.id, p.name, p.description FROM permissions p
		 INNER JOIN role_permissions rp ON p.id = rp.permission_id
		 WHERE rp.role_id = ?`,
		roleID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

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

// GetUserPermissions retrieves all permissions for a user (through their roles)
func (s *RBACStore) GetUserPermissions(ctx context.Context, userID string) ([]Permission, error) {
	rows, err := s.DB.QueryContext(ctx,
		`SELECT DISTINCT p.id, p.name, p.description FROM permissions p
		 INNER JOIN role_permissions rp ON p.id = rp.permission_id
		 INNER JOIN user_roles ur ON rp.role_id = ur.role_id
		 WHERE ur.user_id = ?`,
		userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

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

// UserHasPermission checks if a user has a specific permission
func (s *RBACStore) UserHasPermission(ctx context.Context, userID, permission string) (bool, error) {
	var count int
	err := s.DB.QueryRowContext(ctx,
		`SELECT COUNT(*) FROM permissions p
		 INNER JOIN role_permissions rp ON p.id = rp.permission_id
		 INNER JOIN user_roles ur ON rp.role_id = ur.role_id
		 WHERE ur.user_id = ? AND p.name = ?`,
		userID, permission).Scan(&count)
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

// AssignRoleToUser assigns a role to a user
func (s *RBACStore) AssignRoleToUser(ctx context.Context, userID, roleID string) error {
	_, err := s.DB.ExecContext(ctx,
		`INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)`,
		userID, roleID)
	return err
}

// RemoveRoleFromUser removes a role from a user
func (s *RBACStore) RemoveRoleFromUser(ctx context.Context, userID, roleID string) error {
	_, err := s.DB.ExecContext(ctx,
		`DELETE FROM user_roles WHERE user_id = ? AND role_id = ?`,
		userID, roleID)
	return err
}

// GetAllRoles retrieves all roles
func (s *RBACStore) GetAllRoles(ctx context.Context) ([]Role, error) {
	rows, err := s.DB.QueryContext(ctx,
		`SELECT id, name, description FROM roles ORDER BY name`)
	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); err != nil {
			return nil, err
		}
		roles = append(roles, role)
	}
	return roles, rows.Err()
}

// GetAllPermissions retrieves all permissions
func (s *RBACStore) GetAllPermissions(ctx context.Context) ([]Permission, error) {
	rows, err := s.DB.QueryContext(ctx,
		`SELECT id, name, description FROM permissions ORDER BY name`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

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

// AssignPermissionToRole assigns a permission to a role
func (s *RBACStore) AssignPermissionToRole(ctx context.Context, roleID, permissionID string) error {
	_, err := s.DB.ExecContext(ctx,
		`INSERT OR IGNORE INTO role_permissions (role_id, permission_id) VALUES (?, ?)`,
		roleID, permissionID)
	return err
}

// RemovePermissionFromRole removes a permission from a role
func (s *RBACStore) RemovePermissionFromRole(ctx context.Context, roleID, permissionID string) error {
	_, err := s.DB.ExecContext(ctx,
		`DELETE FROM role_permissions WHERE role_id = ? AND permission_id = ?`,
		roleID, permissionID)
	return err
}
