package schedule

import (
	"context"
	"errors"
	"fmt"
	"os"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/metering/pkg/common/config"
	"github.com/tidbcloud/metering/pkg/networkmetering"
	"github.com/tidbcloud/metering/pkg/repoter"
	"github.com/tidbcloud/metering/test/mocknetworkmetering"
	"github.com/tidbcloud/metering/test/mockrepoter"
	"go.uber.org/mock/gomock"
	"go.uber.org/zap"
)

func MockMetering(metering networkmetering.Metering, repoter repoter.Repoter, err error) (networkmetering.Metering, repoter.Repoter, error) {

	return metering, repoter, err
}

func TestNetworkRun(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	conf := &config.ServiceConfig{
		Vendor: "aws",
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			RerunDays: 1,
		},
	}
	metering := mocknetworkmetering.NewMockMetering(ctrl)
	metering.EXPECT().Metering(gomock.Any(), gomock.Any()).Return("/data/1.db", true, nil).AnyTimes()
	rp := mockrepoter.NewMockRepoter(ctrl)
	rp.EXPECT().Report(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	log, _ := zap.NewDevelopment()
	getMetering = func(_ *config.ServiceConfig, _ *zap.Logger) (networkmetering.Metering, repoter.Repoter, error) {
		return MockMetering(metering, rp, nil)
	}
	network, err := NewNetWorkTask(conf, log)
	assert.Nil(t, err)
	err = network.Run(context.Background())
	assert.Nil(t, err)
	//test run again
	err = network.Run(context.Background())
	assert.Nil(t, err)

}

func TestNetworkRunFailed(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	conf := &config.ServiceConfig{
		Vendor: "aws",
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			RerunDays: 1,
		},
	}
	metering := mocknetworkmetering.NewMockMetering(ctrl)
	metering.EXPECT().Metering(gomock.Any(), gomock.Any()).Return("/data/1.db", true, errors.New("error"))
	rp := mockrepoter.NewMockRepoter(ctrl)
	log, _ := zap.NewDevelopment()
	network, err := NewNetWorkTask(conf, log)
	assert.Nil(t, err)
	getMetering = func(_ *config.ServiceConfig, _ *zap.Logger) (networkmetering.Metering, repoter.Repoter, error) {
		return nil, nil, errors.New("error")
	}
	//test getMetering error
	err = network.Run(context.Background())
	assert.NotNil(t, err)
	//reset getMetering
	getMetering = func(_ *config.ServiceConfig, _ *zap.Logger) (networkmetering.Metering, repoter.Repoter, error) {
		return MockMetering(metering, rp, nil)
	}
	err = network.Run(context.Background())
	assert.NotNil(t, err)
	metering.EXPECT().Metering(gomock.Any(), gomock.Any()).Return("/data/1.db", true, nil)
	rp.EXPECT().Report(gomock.Any(), gomock.Any()).Return(errors.New("error"))
	err = network.Run(context.Background())
	assert.NotNil(t, err)

}

func TestNetworkRerunExternal(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	conf := &config.ServiceConfig{
		Vendor: "aws",
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			RerunDays: 1,
		},
	}
	metering := mocknetworkmetering.NewMockMetering(ctrl)
	rp := mockrepoter.NewMockRepoter(ctrl)
	log, _ := zap.NewDevelopment()
	ts := time.Now().UTC().Unix()
	//test rerun metering failed
	metering.EXPECT().Metering(gomock.Any(), gomock.Any()).Return("", false, errors.New("error"))
	network, err := NewNetWorkTask(conf, log)
	assert.Nil(t, err)
	err = network.ReRunPreDay(context.Background(), metering, rp, ts)
	assert.NotNil(t, err)
	//test rerun report failed
	metering.EXPECT().Metering(gomock.Any(), gomock.Any()).Return("/data/1.db", true, nil)
	rp.EXPECT().Report(gomock.Any(), gomock.Any()).Return(errors.New("error"))
	err = network.ReRunPreDay(context.Background(), metering, rp, ts)
	assert.NotNil(t, err)
	//test rerun success
	metering.EXPECT().Metering(gomock.Any(), gomock.Any()).Return("/data/1.db", true, nil).AnyTimes()
	rp.EXPECT().Report(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	err = network.ReRunPreDay(context.Background(), metering, rp, ts)
	assert.Nil(t, err)
	// test rerun again
	err = network.ReRunPreDay(context.Background(), metering, rp, ts)
	assert.Nil(t, err)
}

func TestNetworkRunNoGetted(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	conf := &config.ServiceConfig{
		Vendor: "aws",
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			RerunDays: 1,
		},
	}
	metering := mocknetworkmetering.NewMockMetering(ctrl)
	metering.EXPECT().Metering(gomock.Any(), gomock.Any()).Return("/data/1.db", false, nil).AnyTimes()
	rp := mockrepoter.NewMockRepoter(ctrl)
	rp.EXPECT().Report(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	log, _ := zap.NewDevelopment()
	getMetering = func(_ *config.ServiceConfig, _ *zap.Logger) (networkmetering.Metering, repoter.Repoter, error) {
		return MockMetering(metering, rp, nil)
	}
	network, err := NewNetWorkTask(conf, log)
	assert.Nil(t, err)
	err = network.Run(context.Background())
	assert.Nil(t, err)
}

func TestRerunDays(t *testing.T) {
	// get UTC time
	now := time.Unix(1732579200, 0).UTC()
	// get the start time of the previous day
	startOfPreviousDay := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, time.UTC)
	ts := startOfPreviousDay.Unix()
	for i := 1; i <= 5; i++ {
		// move to the previous day
		ts -= 24 * 60 * 60
		fmt.Println(ts)
	}
}

func TestGetMetering(t *testing.T) {
	os.Setenv("AWS_ACCESS_KEY_ID", "test")
	os.Setenv("AWS_SECRET_ACCESS_KEY", "test")
	defer os.Unsetenv("AWS_ACCESS_KEY_ID")
	defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
	conf := &config.ServiceConfig{
		Vendor: "aws",
		Env:    "dev",
		Secret: &config.Secret{
			ApiGatewayKey: "test",
		},
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			RerunDays:               1,
			CachePath:               "cachetest_init",
			BillingReportSwitchTime: 0,
			Provider: config.TrafficMeteringProvider{
				AWS: &config.TrafficMeteringProviderAWSConfig{
					StoreProvider: config.StoreProviderAWSConfig{
						RoleArn:    "arn:aws",
						Account:    "123456789012",
						Region:     "us-east-1",
						BucketName: "test-bucket",
						Perfix:     "test-prefix",
					},
					BillingProvider: []config.BillingProviderAWSConfig{{
						RoleArn:    "arn:aws",
						Account:    "123456789012",
						Region:     "us-east-1",
						BucketName: "test-bucket",
						Perfix:     "test-prefix",
						SourceType: "csv",
					},
					},
				},
			},
		},
	}
	log, _ := zap.NewDevelopment()
	metering, repoter, err := GetMetering(conf, log)
	assert.Nil(t, err)
	assert.NotNil(t, metering)
	assert.NotNil(t, repoter)
}
