package schedule

import (
	"fmt"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/metering/pkg/common/config"
)

func TestNewCleanTask(t *testing.T) {
	conf := &config.ServiceConfig{
		InstanceMeteringConfig: &config.InstanceMeteringConfig{
			CleanCacheDelaySecond: 3600,
		},
	}
	task := NewCleanTask(conf)
	assert.NotNil(t, task)
	assert.Equal(t, int64(3600), task.CleanCacheDelaySecond)
	assert.Equal(t, time.Hour*1, task.Interval)
}

func TestCleanup(t *testing.T) {
	// Create temp directory for testing
	tempDir, err := os.MkdirTemp("", "cache-test")
	if err != nil {
		t.Fatalf("Failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(tempDir)

	// Current time
	now := time.Now().Unix()

	// Create test files with different timestamps in the names
	testFiles := []struct {
		name     string
		timeAgo  int64
		expected bool // true if it should be deleted
	}{
		{name: fmt.Sprintf("%d.sqlite", now-7200), timeAgo: now - 7200, expected: true},         // 2 hours ago - should be deleted
		{name: fmt.Sprintf("%d.sqlite", now-1800), timeAgo: now - 1800, expected: false},        // 30 min ago - should remain
		{name: fmt.Sprintf("%d.data.sqlite", now-7200), timeAgo: now - 7200, expected: true},    // 2 hours ago with middle extension - should be deleted
		{name: "invalid.sqlite", timeAgo: 0, expected: false},                                   // Invalid timestamp - should remain
		{name: fmt.Sprintf("%d", now-7200), timeAgo: now - 7200, expected: true},                // No extension - should be deleted
		{name: fmt.Sprintf("%d.sqlite.gz", now-7200), timeAgo: now - 7200, expected: true},      // Multiple extensions - should be deleted
		{name: fmt.Sprintf("%d.sqlite", now-100), timeAgo: now - 100, expected: false},          // Recent - should remain
		{name: fmt.Sprintf("prefix-%d.sqlite", now-7200), timeAgo: now - 7200, expected: false}, // Invalid format - should remain
	}

	// Create the files
	for _, tf := range testFiles {
		filePath := filepath.Join(tempDir, tf.name)
		err := os.WriteFile(filePath, []byte("test"), 0644)
		assert.NoError(t, err)
	}

	// Create a subdirectory to test directory handling
	subDir := filepath.Join(tempDir, "subdir")
	err = os.Mkdir(subDir, 0755)
	assert.NoError(t, err)

	// Create a file in the subdirectory
	subDirFile := filepath.Join(subDir, fmt.Sprintf("%d.sqlite", now-7200))
	err = os.WriteFile(subDirFile, []byte("test"), 0644)
	assert.NoError(t, err)

	// Create the clean task
	task := &CleanTask{
		WatchDir:              tempDir,
		CleanCacheDelaySecond: 3600, // 1 hour threshold
		Interval:              time.Hour,
	}

	// Run cleanup
	task.cleanup()

	// Verify results
	for _, tf := range testFiles {
		filePath := filepath.Join(tempDir, tf.name)
		_, err := os.Stat(filePath)
		if tf.expected {
			// Should be deleted
			assert.True(t, os.IsNotExist(err), "File %s should have been deleted", tf.name)
		} else {
			// Should still exist
			assert.NoError(t, err, "File %s should still exist", tf.name)
		}
	}

	// Verify the file in subdirectory was also deleted
	_, err = os.Stat(subDirFile)
	assert.True(t, os.IsNotExist(err), "File in subdirectory should have been deleted")
}

func TestAsyncTicker(t *testing.T) {
	tempDir, err := os.MkdirTemp("", "async-test")
	if err != nil {
		t.Fatalf("Failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(tempDir)

	// Create a file that should be deleted
	now := time.Now().Unix()
	oldFile := filepath.Join(tempDir, fmt.Sprintf("%d.sqlite", now-7200))
	err = os.WriteFile(oldFile, []byte("test"), 0644)
	assert.NoError(t, err)

	// Create task with short interval for testing
	task := &CleanTask{
		WatchDir:              tempDir,
		CleanCacheDelaySecond: 3600,                   // 1 hour threshold
		Interval:              time.Millisecond * 100, // Very short for testing
	}

	// Start the ticker
	task.AsyncTicker()

	// Wait a bit to let the ticker run at least once
	time.Sleep(time.Millisecond * 150)

	// Check if file was deleted
	_, err = os.Stat(oldFile)
	assert.True(t, os.IsNotExist(err), "File should have been deleted by async ticker")
}
