package store

import "gorm.io/gorm"

func (s *Store) ListVisitors() ([]User, error) {
	var users []User
	err := s.db.Where("role = ?", "visitor").Order("id asc").Find(&users).Error
	return users, err
}

func (s *Store) ListResourceVisitorIDs(resourceType string, resourceID uint) ([]uint, error) {
	var bindings []ResourceVisitor
	err := s.db.Where("resource_type = ? AND resource_id = ?", resourceType, resourceID).
		Order("user_id asc").Find(&bindings).Error
	if err != nil {
		return nil, err
	}
	ids := make([]uint, len(bindings))
	for i, b := range bindings {
		ids[i] = b.UserID
	}
	return ids, nil
}

func (s *Store) SetResourceVisitors(resourceType string, resourceID uint, userIDs []uint) error {
	return s.db.Transaction(func(tx *gorm.DB) error {
		if err := tx.Where("resource_type = ? AND resource_id = ?", resourceType, resourceID).
			Delete(&ResourceVisitor{}).Error; err != nil {
			return err
		}
		for _, uid := range userIDs {
			rv := ResourceVisitor{
				ResourceType: resourceType,
				ResourceID:   resourceID,
				UserID:       uid,
			}
			if err := tx.Create(&rv).Error; err != nil {
				return err
			}
		}
		return nil
	})
}

func (s *Store) DeleteResourceVisitors(resourceType string, resourceID uint) error {
	return s.db.Where("resource_type = ? AND resource_id = ?", resourceType, resourceID).
		Delete(&ResourceVisitor{}).Error
}

func (s *Store) ListAllResourceVisitors() ([]ResourceVisitor, error) {
	var items []ResourceVisitor
	err := s.db.Order("resource_type, resource_id, user_id").Find(&items).Error
	return items, err
}

const MaxVisitorAccessLogsPerUser = 200

func (s *Store) AppendVisitorAccessLog(entry *VisitorAccessLog) error {
	return s.db.Transaction(func(tx *gorm.DB) error {
		if err := tx.Create(entry).Error; err != nil {
			return err
		}
		var keepIDs []uint
		if err := tx.Model(&VisitorAccessLog{}).Where("user_id = ?", entry.UserID).
			Order("id desc").Limit(MaxVisitorAccessLogsPerUser).Pluck("id", &keepIDs).Error; err != nil {
			return err
		}
		if len(keepIDs) == 0 {
			return nil
		}
		return tx.Where("user_id = ? AND id NOT IN ?", entry.UserID, keepIDs).
			Delete(&VisitorAccessLog{}).Error
	})
}

func (s *Store) ListVisitorAccessLogs(userID uint, limit int) ([]VisitorAccessLog, error) {
	if limit <= 0 || limit > MaxVisitorAccessLogsPerUser {
		limit = MaxVisitorAccessLogsPerUser
	}
	var items []VisitorAccessLog
	err := s.db.Where("user_id = ?", userID).
		Order("id desc").Limit(limit).Find(&items).Error
	return items, err
}

func (s *Store) DeleteVisitorAccessLogs(userID uint) error {
	return s.db.Where("user_id = ?", userID).Delete(&VisitorAccessLog{}).Error
}
