package client

import (
	"github.com/google/uuid"
	"gorm.io/gorm"
	"netidhub-saas-be/app/database/entity"
)

// GetAccessibleClientIDs returns all client IDs that a user has access to
// This includes:
// - User's primary client
// - Clients granted via UserClientAccess
// - Sub-clients if IncludeSubClients is true
func GetAccessibleClientIDs(db *gorm.DB, userId uint, isSuperAdmin bool) ([]uuid.UUID, error) {
	if isSuperAdmin {
		// Super admin has access to ALL clients
		return nil, nil // nil means no filter (all clients)
	}

	var clientIDs []uuid.UUID

	// Get user's primary client
	var user entity.Users
	if err := db.Select("client_id").Where("id = ?", userId).First(&user).Error; err == nil {
		if user.ClientId != nil {
			clientIDs = append(clientIDs, *user.ClientId)
		}
	}

	// Get additional client accesses
	var accesses []entity.UserClientAccess
	err := db.Where("user_id = ? AND is_active = ?", userId, true).
		Preload("Client").
		Find(&accesses).Error
	
	if err != nil {
		return clientIDs, err
	}

	for _, access := range accesses {
		clientIDs = append(clientIDs, access.ClientId)

		// If IncludeSubClients is true, get all sub-clients
		if access.IncludeSubClients != nil && *access.IncludeSubClients {
			subClientIDs, err := GetSubClientIDs(db, access.ClientId)
			if err == nil {
				clientIDs = append(clientIDs, subClientIDs...)
			}
		}
	}

	return removeDuplicateUUIDs(clientIDs), nil
}

// GetSubClientIDs returns all sub-client IDs for a given parent client (recursive)
func GetSubClientIDs(db *gorm.DB, parentClientId uuid.UUID) ([]uuid.UUID, error) {
	var clientIDs []uuid.UUID
	var subClients []entity.Clients

	// Get direct children
	err := db.Where("parent_client_id = ? AND is_active = ?", parentClientId, true).
		Find(&subClients).Error
	
	if err != nil {
		return nil, err
	}

	for _, subClient := range subClients {
		clientIDs = append(clientIDs, subClient.ID)

		// Recursively get sub-clients of sub-clients
		nestedSubClientIDs, err := GetSubClientIDs(db, subClient.ID)
		if err == nil {
			clientIDs = append(clientIDs, nestedSubClientIDs...)
		}
	}

	return clientIDs, nil
}

// GetClientHierarchy returns the full client hierarchy (parent chain and children)
func GetClientHierarchy(db *gorm.DB, clientId uuid.UUID) (*entity.Clients, error) {
	var client entity.Clients
	
	err := db.Preload("ParentClient").
		Preload("SubClients", "is_active = ?", true).
		Where("id = ?", clientId).
		First(&client).Error
	
	return &client, err
}

// HasAccessToClient checks if a user has access to a specific client
func HasAccessToClient(db *gorm.DB, userId uint, clientId uuid.UUID, isSuperAdmin bool) (bool, error) {
	if isSuperAdmin {
		return true, nil
	}

	accessibleClientIDs, err := GetAccessibleClientIDs(db, userId, isSuperAdmin)
	if err != nil {
		return false, err
	}

	// nil means all clients (super admin case)
	if accessibleClientIDs == nil {
		return true, nil
	}

	for _, id := range accessibleClientIDs {
		if id == clientId {
			return true, nil
		}
	}

	return false, nil
}

// GetParentClientID returns the root/parent client ID for a given client
func GetParentClientID(db *gorm.DB, clientId uuid.UUID) (*uuid.UUID, error) {
	var client entity.Clients
	
	err := db.Preload("ParentClient").Where("id = ?", clientId).First(&client).Error
	if err != nil {
		return nil, err
	}

	// If has parent, recursively get the root parent
	if client.ParentClientId != nil {
		return GetParentClientID(db, *client.ParentClientId)
	}

	// This is the root parent
	return &client.ID, nil
}

// IsParentClient checks if a client is a parent client (has sub-clients)
func IsParentClient(db *gorm.DB, clientId uuid.UUID) (bool, error) {
	var count int64
	err := db.Model(&entity.Clients{}).
		Where("parent_client_id = ? AND is_active = ?", clientId, true).
		Count(&count).Error
	
	return count > 0, err
}

// removeDuplicateUUIDs removes duplicate UUIDs from a slice
func removeDuplicateUUIDs(uuids []uuid.UUID) []uuid.UUID {
	encountered := map[uuid.UUID]bool{}
	result := []uuid.UUID{}

	for _, uuid := range uuids {
		if !encountered[uuid] {
			encountered[uuid] = true
			result = append(result, uuid)
		}
	}

	return result
}
