package repository

import (
	"context"
	"time"

	"gorm.io/gorm"

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

// LoginLogRepository 登录日志仓储
type LoginLogRepository struct {
	db *gorm.DB
}

// NewLoginLogRepository 创建登录日志仓储
func NewLoginLogRepository(db *gorm.DB) *LoginLogRepository {
	return &LoginLogRepository{db: db}
}

// Create 创建登录日志
func (r *LoginLogRepository) Create(ctx context.Context, log *domain.LoginLog) error {
	return r.db.WithContext(ctx).Create(log).Error
}

// GetByID 根据ID获取登录日志
func (r *LoginLogRepository) GetByID(ctx context.Context, id int64) (*domain.LoginLog, error) {
	var log domain.LoginLog
	if err := r.db.WithContext(ctx).First(&log, id).Error; err != nil {
		return nil, err
	}
	return &log, nil
}

// ListByUserID 获取用户的登录日志列表
func (r *LoginLogRepository) ListByUserID(ctx context.Context, userID int64, offset, limit int) ([]*domain.LoginLog, int64, error) {
	var logs []*domain.LoginLog
	var total int64
	query := r.db.WithContext(ctx).Model(&domain.LoginLog{}).Where("user_id = ?", userID)
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if err := query.Order("created_at DESC").Offset(offset).Limit(limit).Find(&logs).Error; err != nil {
		return nil, 0, err
	}
	return logs, total, nil
}

// List 获取登录日志列表（管理员用）
func (r *LoginLogRepository) List(ctx context.Context, offset, limit int) ([]*domain.LoginLog, int64, error) {
	var logs []*domain.LoginLog
	var total int64
	query := r.db.WithContext(ctx).Model(&domain.LoginLog{})
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if err := query.Order("created_at DESC").Offset(offset).Limit(limit).Find(&logs).Error; err != nil {
		return nil, 0, err
	}
	return logs, total, nil
}

// ListByStatus 按状态查询登录日志
func (r *LoginLogRepository) ListByStatus(ctx context.Context, status int, offset, limit int) ([]*domain.LoginLog, int64, error) {
	var logs []*domain.LoginLog
	var total int64
	query := r.db.WithContext(ctx).Model(&domain.LoginLog{}).Where("status = ?", status)
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if err := query.Order("created_at DESC").Offset(offset).Limit(limit).Find(&logs).Error; err != nil {
		return nil, 0, err
	}
	return logs, total, nil
}

// ListByTimeRange 按时间范围查询登录日志
func (r *LoginLogRepository) ListByTimeRange(ctx context.Context, start, end time.Time, offset, limit int) ([]*domain.LoginLog, int64, error) {
	var logs []*domain.LoginLog
	var total int64
	query := r.db.WithContext(ctx).Model(&domain.LoginLog{}).
		Where("created_at >= ? AND created_at <= ?", start, end)
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if err := query.Order("created_at DESC").Offset(offset).Limit(limit).Find(&logs).Error; err != nil {
		return nil, 0, err
	}
	return logs, total, nil
}

// DeleteByUserID 删除用户所有登录日志
func (r *LoginLogRepository) DeleteByUserID(ctx context.Context, userID int64) error {
	return r.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&domain.LoginLog{}).Error
}

// DeleteOlderThan 删除指定天数前的日志
func (r *LoginLogRepository) DeleteOlderThan(ctx context.Context, days int) error {
	cutoff := time.Now().AddDate(0, 0, -days)
	return r.db.WithContext(ctx).Where("created_at < ?", cutoff).Delete(&domain.LoginLog{}).Error
}

// CountByResultSince 统计指定时间之后特定结果的登录次数
// success=true 统计成功次数，false 统计失败次数
func (r *LoginLogRepository) CountByResultSince(ctx context.Context, success bool, since time.Time) int64 {
	status := 0 // 失败
	if success {
		status = 1 // 成功
	}
	var count int64
	r.db.WithContext(ctx).Model(&domain.LoginLog{}).
		Where("status = ? AND created_at >= ?", status, since).
		Count(&count)
	return count
}

// ListAllForExport 获取所有登录日志（用于导出，无分页）
func (r *LoginLogRepository) ListAllForExport(ctx context.Context, userID int64, status int, startAt, endAt *time.Time) ([]*domain.LoginLog, error) {
	var logs []*domain.LoginLog
	query := r.db.WithContext(ctx).Model(&domain.LoginLog{})

	if userID > 0 {
		query = query.Where("user_id = ?", userID)
	}
	if status == 0 || status == 1 {
		query = query.Where("status = ?", status)
	}
	if startAt != nil {
		query = query.Where("created_at >= ?", startAt)
	}
	if endAt != nil {
		query = query.Where("created_at <= ?", endAt)
	}

	if err := query.Order("created_at DESC").Find(&logs).Error; err != nil {
		return nil, err
	}
	return logs, nil
}

// ExportBatchSize 单次导出的最大记录数
const ExportBatchSize = 100000

// ListLogsForExportBatch 分批获取登录日志（用于流式导出）
// cursor 是上一次最后一条记录的 ID，limit 是每批数量
func (r *LoginLogRepository) ListLogsForExportBatch(ctx context.Context, userID int64, status int, startAt, endAt *time.Time, cursor int64, limit int) ([]*domain.LoginLog, bool, error) {
	var logs []*domain.LoginLog
	query := r.db.WithContext(ctx).Model(&domain.LoginLog{}).Where("id < ?", cursor)

	if userID > 0 {
		query = query.Where("user_id = ?", userID)
	}
	if status == 0 || status == 1 {
		query = query.Where("status = ?", status)
	}
	if startAt != nil {
		query = query.Where("created_at >= ?", startAt)
	}
	if endAt != nil {
		query = query.Where("created_at <= ?", endAt)
	}

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

	hasMore := len(logs) == limit
	return logs, hasMore, nil
}

// ListCursor 游标分页查询登录日志（管理员用）
// Uses keyset pagination: WHERE (created_at < ? OR (created_at = ? AND id < ?))
// This avoids the O(offset) deep-pagination problem of OFFSET/LIMIT.
func (r *LoginLogRepository) ListCursor(ctx context.Context, limit int, cursor *pagination.Cursor) ([]*domain.LoginLog, bool, error) {
	var logs []*domain.LoginLog

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

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

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

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

// ListByUserIDCursor 按用户ID游标分页查询登录日志
func (r *LoginLogRepository) ListByUserIDCursor(ctx context.Context, userID int64, limit int, cursor *pagination.Cursor) ([]*domain.LoginLog, bool, error) {
	var logs []*domain.LoginLog

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

	if cursor != nil && cursor.LastID > 0 {
		query = query.Where(
			"(created_at < ? OR (created_at = ? AND id < ?))",
			cursor.LastValue, cursor.LastValue, cursor.LastID,
		)
	}

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

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