package process

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

	"github.com/prometheus/client_golang/prometheus/testutil"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/observability/cmd/diagnosis-compute/pkg/config"
	"github.com/tidbcloud/observability/cmd/diagnosis-compute/pkg/metrics"
	"github.com/tidbcloud/observability/cmd/diagnosis-compute/pkg/types"
	"golang.org/x/sync/semaphore"
)

func TestRegionFileTime(t *testing.T) {
	ts, ok := regionFileTime("202602040000.json.log.gz")
	require.True(t, ok)
	assert.Equal(t, time.Date(2026, 2, 4, 0, 0, 0, 0, time.UTC).Unix(), ts)

	ts, ok = regionFileTime("202602041500.json.gz")
	require.True(t, ok)
	assert.Equal(t, time.Date(2026, 2, 4, 15, 0, 0, 0, time.UTC).Unix(), ts)

	_, ok = regionFileTime("not-a-region-file.log.gz")
	assert.False(t, ok)
}

func TestUnixTsFromFilename(t *testing.T) {
	ts, ok := unixTsFromFilename("1772064000-pd-cpu-ZGItcGQtMC.log.gz")
	require.True(t, ok)
	assert.Equal(t, int64(1772064000), ts)

	_, ok = unixTsFromFilename("short.log")
	assert.False(t, ok)
}

func TestRecordHourlyAuxRawStats(t *testing.T) {
	storeDir := t.TempDir()
	config.LocalMergedFileDir = filepath.Join(storeDir, "tmp")
	config.S3sem = semaphore.NewWeighted(10)
	config.ClusterTaskSem = semaphore.NewWeighted(10)
	store := NewMockStorage2(storeDir)
	ctx := context.Background()

	info := types.ClusterInfo{
		Type:        0,
		Env:         "prod",
		TenantID:    "tenant-aux",
		ProjectID:   "project-aux",
		ClusterID:   "cluster-aux",
		StorageID:   "cluster-aux",
		ClusterType: types.ClusterTypeDedicated,
	}

	hourBegin := time.Date(2026, 2, 4, 0, 0, 0, 0, time.UTC).Unix()
	hourEnd := hourBegin + hourSeconds - 1
	inHourProfileTS := hourBegin + 120
	outHourProfileTS := hourBegin + hourSeconds + 10

	profilesPrefix := getProfilesPrefix(info)
	regionsPrefix := getRegionsPrefix(info)

	require.NoError(t, store.SaveObject(ctx, config.BucketName(),
		fmt.Sprintf("%s%d-pd-cpu-in.log.gz", profilesPrefix, inHourProfileTS),
		strings.NewReader("profile-in")))
	require.NoError(t, store.SaveObject(ctx, config.BucketName(),
		fmt.Sprintf("%s%d-pd-cpu-out.log.gz", profilesPrefix, outHourProfileTS),
		strings.NewReader("profile-out-should-skip")))
	require.NoError(t, store.SaveObject(ctx, config.BucketName(),
		regionsPrefix+"202602040000.json.log.gz",
		strings.NewReader("region-hour")))
	require.NoError(t, store.SaveObject(ctx, config.BucketName(),
		regionsPrefix+"202602040100.json.log.gz",
		strings.NewReader("region-next-hour-should-skip")))

	// Reset series so the test is deterministic across runs in the same process.
	metrics.RawLogFiles.Reset()
	metrics.RawLogBytes.Reset()

	recordHourlyAuxRawStats(ctx, store, config.BucketName(), info, hourBegin, hourEnd)

	assert.InDelta(t, 1.0, testutil.ToFloat64(metrics.RawLogFiles.WithLabelValues("prod", "tenant-aux", "cluster-aux", "profiles")), 0.001)
	assert.InDelta(t, float64(len("profile-in")), testutil.ToFloat64(metrics.RawLogBytes.WithLabelValues("prod", "tenant-aux", "cluster-aux", "profiles")), 0.001)
	assert.InDelta(t, 1.0, testutil.ToFloat64(metrics.RawLogFiles.WithLabelValues("prod", "tenant-aux", "cluster-aux", "regions")), 0.001)
	assert.InDelta(t, float64(len("region-hour")), testutil.ToFloat64(metrics.RawLogBytes.WithLabelValues("prod", "tenant-aux", "cluster-aux", "regions")), 0.001)

	// Ensure fixture files exist on disk for debugging failures.
	_, err := os.Stat(filepath.Join(storeDir, config.BucketName(), filepath.FromSlash(fmt.Sprintf("%s%d-pd-cpu-in.log.gz", profilesPrefix, inHourProfileTS))))
	require.NoError(t, err)
}

func TestGetClusterDataPrefix(t *testing.T) {
	info := types.ClusterInfo{
		Type: 0, TenantID: "t", ProjectID: "p", ClusterID: "c",
		ClusterType: types.ClusterTypeDedicated,
	}
	assert.Equal(t, "0/t/p/c/logs/", getLogPrefix(info))
	assert.Equal(t, "0/t/p/c/profiles/", getProfilesPrefix(info))
	assert.Equal(t, "0/t/p/c/regions/", getRegionsPrefix(info))
}
