// monorepo/cloud/maplefile-backend/internal/usecase/user/quota_helper.go
package user

import (
	"context"
	"fmt"
	"time"

	"github.com/gocql/gocql"
	"go.uber.org/zap"

	dom_storagedailyusage "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/storagedailyusage"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
)

// Default storage quota limit: 10GB
const DefaultStorageQuotaBytes int64 = 10 * 1024 * 1024 * 1024

// UserStorageQuotaHelperUseCase provides storage quota validation
type UserStorageQuotaHelperUseCase interface {
	HasEnoughQuota(ctx context.Context, userID gocql.UUID, sizeBytes int64) (bool, error)
	CheckAndReserveQuota(ctx context.Context, userID gocql.UUID, sizeBytes int64) error
	ReleaseQuota(ctx context.Context, userID gocql.UUID, sizeBytes int64) error
	OnFileDeleted(ctx context.Context, userID gocql.UUID, sizeBytes int64) error
}

type userStorageQuotaHelperUseCaseImpl struct {
	logger                      *zap.Logger
	storageDailyUsageRepository dom_storagedailyusage.StorageDailyUsageRepository
}

// NewUserStorageQuotaHelperUseCase creates a new storage quota helper use case
func NewUserStorageQuotaHelperUseCase(
	logger *zap.Logger,
	storageDailyUsageRepository dom_storagedailyusage.StorageDailyUsageRepository,
) UserStorageQuotaHelperUseCase {
	return &userStorageQuotaHelperUseCaseImpl{
		logger:                      logger.Named("UserStorageQuotaHelper"),
		storageDailyUsageRepository: storageDailyUsageRepository,
	}
}

// HasEnoughQuota checks if user has enough storage quota
func (uc *userStorageQuotaHelperUseCaseImpl) HasEnoughQuota(ctx context.Context, userID gocql.UUID, sizeBytes int64) (bool, error) {
	// Get current storage usage from most recent day
	today := time.Now().UTC().Truncate(24 * time.Hour)
	usage, err := uc.storageDailyUsageRepository.GetByUserAndDay(ctx, userID, today)

	var currentUsage int64 = 0
	if err == nil && usage != nil {
		currentUsage = usage.TotalBytes
	}

	// Check if adding the new size would exceed the quota
	newTotal := currentUsage + sizeBytes
	hasQuota := newTotal <= DefaultStorageQuotaBytes

	uc.logger.Debug("Quota check",
		zap.String("user_id", userID.String()),
		zap.Int64("current_usage", currentUsage),
		zap.Int64("requested_size", sizeBytes),
		zap.Int64("new_total", newTotal),
		zap.Int64("quota_limit", DefaultStorageQuotaBytes),
		zap.Bool("has_quota", hasQuota))

	return hasQuota, nil
}

// CheckAndReserveQuota reserves storage quota for a user
func (uc *userStorageQuotaHelperUseCaseImpl) CheckAndReserveQuota(ctx context.Context, userID gocql.UUID, sizeBytes int64) error {
	hasQuota, err := uc.HasEnoughQuota(ctx, userID, sizeBytes)
	if err != nil {
		uc.logger.Error("Failed to check quota",
			zap.String("user_id", userID.String()),
			zap.Int64("size_bytes", sizeBytes),
			zap.Error(err))
		return httperror.NewForInternalServerErrorWithSingleField("message", "Failed to check storage quota")
	}

	if !hasQuota {
		uc.logger.Warn("User exceeded storage quota",
			zap.String("user_id", userID.String()),
			zap.Int64("requested_size", sizeBytes))

		return httperror.NewForBadRequestWithSingleField(
			"storage_quota",
			fmt.Sprintf("Storage quota exceeded. You are trying to upload %d bytes, but your quota limit is %d GB. Please delete some files or upgrade your plan.",
				sizeBytes,
				DefaultStorageQuotaBytes/(1024*1024*1024)))
	}

	// Note: Actual quota reservation would be tracked in a separate table
	// For now, we just validate and rely on the storage events to track actual usage
	uc.logger.Info("Quota check passed",
		zap.String("user_id", userID.String()),
		zap.Int64("size_bytes", sizeBytes))

	return nil
}

// ReleaseQuota releases previously reserved storage quota
func (uc *userStorageQuotaHelperUseCaseImpl) ReleaseQuota(ctx context.Context, userID gocql.UUID, sizeBytes int64) error {
	// Note: In a full implementation, this would release a reservation
	// For now, we just log the release since we're not tracking reservations separately
	uc.logger.Debug("Quota release requested",
		zap.String("user_id", userID.String()),
		zap.Int64("size_bytes", sizeBytes))
	return nil
}

// OnFileDeleted handles quota updates when a file is deleted
func (uc *userStorageQuotaHelperUseCaseImpl) OnFileDeleted(ctx context.Context, userID gocql.UUID, sizeBytes int64) error {
	// Note: This is a no-op because storage usage tracking is handled by storage events
	// The actual storage decrease is recorded via IncrementUsage with negative values
	uc.logger.Debug("File deleted notification",
		zap.String("user_id", userID.String()),
		zap.Int64("size_bytes", sizeBytes))
	return nil
}
