package models

import (
	"crypto/md5"
	"encoding/hex"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/observability/o11ymetering/pkg/common/constants"
	"gorm.io/gorm"
)

func TestStatisticRecord_BuildID(t *testing.T) {
	// Create a StatisticRecord instance with test data.
	sr := StatisticRecord{
		Period:        "202407231000",
		Region:        "us-west-2",
		Env:           constants.Staging,
		MeteringGroup: constants.CloudPlatform,
		StatisticName: constants.MetricsTimeseriesCount,
		Category:      constants.Metrics,
		SubCategory:   "some_subcategory",
		ClusterMeta: ClusterMeta{
			Tenant:  "tenant-a",
			Project: "project-b",
			Cluster: "cluster-c",
		},
		Component: "component-d",
		Container: "container-e",
		Source:    "source-f",
	}

	// Manually build the expected ID string.
	strs := []string{
		sr.Period,
		sr.Region,
		string(sr.Env),
		string(sr.MeteringGroup),
		string(sr.StatisticName),
		string(sr.Category),
		sr.SubCategory,
		sr.ClusterMeta.Tenant,
		sr.ClusterMeta.Project,
		sr.ClusterMeta.Cluster,
		sr.Component,
		sr.Container,
		sr.Source,
	}
	text := strings.Join(strs, "-")
	hash := md5.Sum([]byte(text))
	expectedID := hex.EncodeToString(hash[:])

	// Call the BuildID method and assert the ID is as expected.
	actualID := sr.BuildID()
	assert.Equal(t, expectedID, actualID, "BuildID should generate the correct MD5 hash for StatisticRecord")
	assert.Equal(t, expectedID, sr.ID, "BuildID should set the ID field of the struct")
}

func TestStatisticRecord_BeforeCreate(t *testing.T) {
	// Create a StatisticRecord instance with an empty ID and a period string.
	sr := StatisticRecord{
		ID:            "", // Ensure ID is empty for BeforeCreate to generate it
		Period:        "2024-07-23 10:00:00",
		Region:        "us-west-2",
		Env:           constants.Staging,
		MeteringGroup: constants.CloudPlatform,
		StatisticName: constants.MetricsTimeseriesCount,
		Category:      constants.Metrics,
		SubCategory:   "some_subcategory",
		ClusterMeta: ClusterMeta{
			Tenant:  "tenant-a",
			Project: "project-b",
			Cluster: "cluster-c",
		},
		Component: "component-d",
		Container: "container-e",
		Source:    "source-f",
	}

	// Call BeforeCreate.
	err := sr.BeforeCreate(&gorm.DB{})
	assert.NoError(t, err, "BeforeCreate should not return an error")
	assert.NotEmpty(t, sr.ID, "BeforeCreate should generate an ID if it's empty")

	// Verify PeriodTime is set correctly.
	location, err := time.LoadLocation("UTC")
	assert.NoError(t, err)
	expectedPeriodTime, _ := time.ParseInLocation("2006-01-02 15:04:05", "2024-07-23 10:00:00", location)
	assert.Equal(t, expectedPeriodTime, sr.PeriodTime, "BeforeCreate should set PeriodTime correctly")

	// Test case where ID is already set.
	originalID := "existing-record-id"
	srWithID := StatisticRecord{
		ID:            originalID,
		Period:        "2024-07-23 11:00:00",
		Region:        "us-west-2",
		Env:           constants.Staging,
		MeteringGroup: constants.CloudPlatform,
		StatisticName: constants.MetricsTimeseriesCount,
	}
	err = srWithID.BeforeCreate(&gorm.DB{})
	assert.NoError(t, err, "BeforeCreate should not return an error when ID is already set")
	assert.Equal(t, originalID, srWithID.ID, "BeforeCreate should not change the ID if it's already set")
	expectedPeriodTimeWithID, _ := time.ParseInLocation("2006-01-02 15:04:05", "2024-07-23 11:00:00", location)
	assert.Equal(t, expectedPeriodTimeWithID, srWithID.PeriodTime, "BeforeCreate should still set PeriodTime correctly even if ID is present")
}

func TestStatisticTask_BuildID(t *testing.T) {
	// Create a StatisticTask instance with test data.
	st := StatisticTask{
		StatisticClass: constants.MetricsCollection,
		Region:         "eu-central-1",
		Period:         "2024-07-23 12:00:00",
		Env:            constants.Dev,
		MeteringGroup:  constants.O11Y,
	}

	// Manually build the expected ID string.
	strs := []string{
		string(st.StatisticClass),
		st.Region,
		st.Period,
		string(st.Env),
		string(st.MeteringGroup),
	}
	text := strings.Join(strs, "-")
	hash := md5.Sum([]byte(text))
	expectedID := hex.EncodeToString(hash[:])

	// Call the BuildID method and assert the ID is as expected.
	actualID := st.BuildID()
	assert.Equal(t, expectedID, actualID, "BuildID should generate the correct MD5 hash for StatisticTask")
	assert.Equal(t, expectedID, st.ID, "BuildID should set the ID field of the struct")
}

func TestStatisticTask_BeforeCreate(t *testing.T) {
	// Create a StatisticTask instance with an empty ID.
	st := StatisticTask{
		ID:             "", // Ensure ID is empty for BeforeCreate to generate it
		StatisticClass: constants.MetricsCollection,
		Region:         "eu-central-1",
		Period:         "2024-07-23 12:00:00",
		Env:            constants.Dev,
		MeteringGroup:  constants.O11Y,
	}

	// Call BeforeCreate.
	err := st.BeforeCreate(&gorm.DB{})
	assert.NoError(t, err, "BeforeCreate should not return an error")
	assert.NotEmpty(t, st.ID, "BeforeCreate should generate an ID if it's empty")

	// Test case where ID is already set.
	originalID := "existing-task-id"
	stWithID := StatisticTask{
		ID:             originalID,
		StatisticClass: constants.MetricsCollection,
		Region:         "eu-central-1",
		Period:         "2024-07-23 13:00:00",
		Env:            constants.Dev,
		MeteringGroup:  constants.O11Y,
	}
	err = stWithID.BeforeCreate(&gorm.DB{})
	assert.NoError(t, err, "BeforeCreate should not return an error when ID is already set")
	assert.Equal(t, originalID, stWithID.ID, "BeforeCreate should not change the ID if it's already set")
}
