package repository

import (
	"context"
	"time"

	"gorm.io/gorm"

	"github.com/user-management-system/internal/domain"
	"github.com/user-management-system/internal/pagination"
)

// DeviceRepository 设备数据访问层
type DeviceRepository struct {
	db *gorm.DB
}

// NewDeviceRepository 创建设备数据访问层
func NewDeviceRepository(db *gorm.DB) *DeviceRepository {
	return &DeviceRepository{db: db}
}

// Create 创建设备
func (r *DeviceRepository) Create(ctx context.Context, device *domain.Device) error {
	// GORM omits zero values on insert for fields with DB defaults. Explicitly
	// backfill inactive status so callers can persist status=0 devices.
	requestedStatus := device.Status
	return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if err := tx.Create(device).Error; err != nil {
			return err
		}
		if requestedStatus == domain.DeviceStatusInactive {
			if err := tx.Model(&domain.Device{}).Where("id = ?", device.ID).Update("status", requestedStatus).Error; err != nil {
				return err
			}
			device.Status = requestedStatus
		}
		return nil
	})
}

// Update 更新设备
func (r *DeviceRepository) Update(ctx context.Context, device *domain.Device) error {
	return r.db.WithContext(ctx).Save(device).Error
}

// Delete 删除设备
func (r *DeviceRepository) Delete(ctx context.Context, id int64) error {
	return r.db.WithContext(ctx).Delete(&domain.Device{}, id).Error
}

// GetByID 根据ID获取设备
func (r *DeviceRepository) GetByID(ctx context.Context, id int64) (*domain.Device, error) {
	var device domain.Device
	err := r.db.WithContext(ctx).First(&device, id).Error
	if err != nil {
		return nil, err
	}
	return &device, nil
}

// GetByDeviceID 根据设备ID和用户ID获取设备
func (r *DeviceRepository) GetByDeviceID(ctx context.Context, userID int64, deviceID string) (*domain.Device, error) {
	var device domain.Device
	err := r.db.WithContext(ctx).Where("user_id = ? AND device_id = ?", userID, deviceID).First(&device).Error
	if err != nil {
		return nil, err
	}
	return &device, nil
}

// List 获取设备列表
func (r *DeviceRepository) List(ctx context.Context, offset, limit int) ([]*domain.Device, int64, error) {
	var devices []*domain.Device
	var total int64

	query := r.db.WithContext(ctx).Model(&domain.Device{})

	// 获取总数
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// 获取列表
	if err := query.Offset(offset).Limit(limit).Find(&devices).Error; err != nil {
		return nil, 0, err
	}

	return devices, total, nil
}

// ListByUserID 根据用户ID获取设备列表
func (r *DeviceRepository) ListByUserID(ctx context.Context, userID int64, offset, limit int) ([]*domain.Device, int64, error) {
	var devices []*domain.Device
	var total int64

	query := r.db.WithContext(ctx).Model(&domain.Device{}).Where("user_id = ?", userID)

	// 获取总数
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// 获取列表
	if err := query.Offset(offset).Limit(limit).Order("last_active_time DESC").Find(&devices).Error; err != nil {
		return nil, 0, err
	}

	return devices, total, nil
}

// ListByStatus 根据状态获取设备列表
func (r *DeviceRepository) ListByStatus(ctx context.Context, status domain.DeviceStatus, offset, limit int) ([]*domain.Device, int64, error) {
	var devices []*domain.Device
	var total int64

	query := r.db.WithContext(ctx).Model(&domain.Device{}).Where("status = ?", status)

	// 获取总数
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// 获取列表
	if err := query.Offset(offset).Limit(limit).Find(&devices).Error; err != nil {
		return nil, 0, err
	}

	return devices, total, nil
}

// UpdateStatus 更新设备状态
func (r *DeviceRepository) UpdateStatus(ctx context.Context, id int64, status domain.DeviceStatus) error {
	return r.db.WithContext(ctx).Model(&domain.Device{}).Where("id = ?", id).Update("status", status).Error
}

// UpdateLastActiveTime 更新最后活跃时间
func (r *DeviceRepository) UpdateLastActiveTime(ctx context.Context, id int64) error {
	now := time.Now()
	return r.db.WithContext(ctx).Model(&domain.Device{}).Where("id = ?", id).Update("last_active_time", now).Error
}

// Exists 检查设备是否存在
func (r *DeviceRepository) Exists(ctx context.Context, userID int64, deviceID string) (bool, error) {
	var count int64
	err := r.db.WithContext(ctx).Model(&domain.Device{}).
		Where("user_id = ? AND device_id = ?", userID, deviceID).
		Count(&count).Error
	return count > 0, err
}

// DeleteByUserID 删除用户的所有设备
func (r *DeviceRepository) DeleteByUserID(ctx context.Context, userID int64) error {
	return r.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&domain.Device{}).Error
}

// GetActiveDevices 获取活跃设备
func (r *DeviceRepository) GetActiveDevices(ctx context.Context, userID int64) ([]*domain.Device, error) {
	var devices []*domain.Device
	thirtyDaysAgo := time.Now().Add(-30 * 24 * time.Hour)
	err := r.db.WithContext(ctx).
		Where("user_id = ? AND last_active_time > ?", userID, thirtyDaysAgo).
		Order("last_active_time DESC").
		Find(&devices).Error
	if err != nil {
		return nil, err
	}
	return devices, nil
}

// TrustDevice 设置设备为信任状态
func (r *DeviceRepository) TrustDevice(ctx context.Context, deviceID int64, expiresAt *time.Time) error {
	updates := map[string]interface{}{
		"is_trusted":       true,
		"trust_expires_at": expiresAt,
	}
	return r.db.WithContext(ctx).Model(&domain.Device{}).Where("id = ?", deviceID).Updates(updates).Error
}

// UntrustDevice 取消设备信任状态
func (r *DeviceRepository) UntrustDevice(ctx context.Context, deviceID int64) error {
	updates := map[string]interface{}{
		"is_trusted":       false,
		"trust_expires_at": nil,
	}
	return r.db.WithContext(ctx).Model(&domain.Device{}).Where("id = ?", deviceID).Updates(updates).Error
}

// DeleteAllByUserIDExcept 删除用户的所有设备（除指定设备外）
func (r *DeviceRepository) DeleteAllByUserIDExcept(ctx context.Context, userID int64, exceptDeviceID int64) error {
	return r.db.WithContext(ctx).
		Where("user_id = ? AND id != ?", userID, exceptDeviceID).
		Delete(&domain.Device{}).Error
}

// GetTrustedDevices 获取用户的信任设备列表
func (r *DeviceRepository) GetTrustedDevices(ctx context.Context, userID int64) ([]*domain.Device, error) {
	var devices []*domain.Device
	now := time.Now()
	err := r.db.WithContext(ctx).
		Where("user_id = ? AND is_trusted = ? AND (trust_expires_at IS NULL OR trust_expires_at > ?)", userID, true, now).
		Order("last_active_time DESC").
		Find(&devices).Error
	if err != nil {
		return nil, err
	}
	return devices, nil
}

// ListDevicesParams 设备列表查询参数
type ListDevicesParams struct {
	UserID    int64
	Status    *domain.DeviceStatus // nil-不筛选, 0-禁用, 1-激活
	IsTrusted *bool
	Keyword   string
	Offset    int
	Limit     int
}

// ListAll 获取所有设备列表（支持筛选）
func (r *DeviceRepository) ListAll(ctx context.Context, params *ListDevicesParams) ([]*domain.Device, int64, error) {
	var devices []*domain.Device
	var total int64

	query := r.db.WithContext(ctx).Model(&domain.Device{})

	// 按用户ID筛选
	if params.UserID > 0 {
		query = query.Where("user_id = ?", params.UserID)
	}
	// 按状态筛选
	if params.Status != nil {
		query = query.Where("status = ?", *params.Status)
	}
	// 按信任状态筛选
	if params.IsTrusted != nil {
		query = query.Where("is_trusted = ?", *params.IsTrusted)
	}
	// 按关键词筛选（设备名/IP/位置）- 转义 LIKE 特殊字符
	if params.Keyword != "" {
		escapedKeyword := escapeLikePattern(params.Keyword)
		pattern := "%" + escapedKeyword + "%"
		query = query.Where("device_name LIKE ? OR ip LIKE ? OR location LIKE ?", pattern, pattern, pattern)
	}

	// 获取总数
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// 获取列表
	if err := query.Offset(params.Offset).Limit(params.Limit).
		Order("last_active_time DESC").Find(&devices).Error; err != nil {
		return nil, 0, err
	}

	return devices, total, nil
}

// ListAllCursor 游标分页查询所有设备（支持筛选）
// Sort column: last_active_time DESC, id DESC
func (r *DeviceRepository) ListAllCursor(ctx context.Context, params *ListDevicesParams, limit int, cursor *pagination.Cursor) ([]*domain.Device, bool, error) {
	var devices []*domain.Device

	query := r.db.WithContext(ctx).Model(&domain.Device{})

	// Apply filters
	if params.UserID > 0 {
		query = query.Where("user_id = ?", params.UserID)
	}
	if params.Status != nil {
		query = query.Where("status = ?", *params.Status)
	}
	if params.IsTrusted != nil {
		query = query.Where("is_trusted = ?", *params.IsTrusted)
	}
	if params.Keyword != "" {
		escapedKeyword := escapeLikePattern(params.Keyword)
		pattern := "%" + escapedKeyword + "%"
		query = query.Where("device_name LIKE ? ESCAPE '\\' OR ip LIKE ? ESCAPE '\\' OR location LIKE ? ESCAPE '\\'", pattern, pattern, pattern)
	}

	// Apply cursor condition for keyset navigation
	if cursor != nil && cursor.LastID > 0 {
		query = query.Where(
			"(last_active_time < ? OR (last_active_time = ? AND id < ?))",
			cursor.LastValue, cursor.LastValue, cursor.LastID,
		)
	}

	if err := query.Order("last_active_time DESC, id DESC").Limit(limit + 1).Find(&devices).Error; err != nil {
		return nil, false, err
	}

	hasMore := len(devices) > limit
	if hasMore {
		devices = devices[:limit]
	}
	return devices, hasMore, nil
}
