package schedule

import (
	"context"
	goerr "errors"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/metering/pkg/errors"
	"github.com/tidbcloud/metering/pkg/facade"
	"github.com/tidbcloud/metering/test/mockfacade"
	"go.uber.org/mock/gomock"
)

func TestTask_Execute(t *testing.T) {
	ctrl := gomock.NewController(t)
	rw := mockfacade.NewMockCheckpointReaderWriter(ctrl)
	facade.MockCheckpointReaderWriter(rw)

	syncRW := mockfacade.NewMockScheduleTaskDataSync(ctrl)
	facade.MockScheduleTaskDataSync(syncRW)

	syncRW.EXPECT().Upload(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	rw.EXPECT().SaveCheckpoint(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()

	rw.EXPECT().GetCheckpoint(gomock.Any(), gomock.Any()).Return(int64(1111110060), nil).Times(1)

	rw.EXPECT().GetCheckpoint(gomock.Any(), gomock.Any()).Return(int64(0), errors.New(errors.S3Error, "")).AnyTimes()

	task := Task{
		Name:        "test",
		start:       0,
		interval:    time.Second,
		immediately: true,
		nextTimestamp: func(ctx context.Context, checkpoint int64, start int64) int64 {
			return checkpoint + 1
		},
		ready: func(ctx context.Context, timestamp int64) bool {
			return true
		},
		execute: func(ctx context.Context, timestamp int64) error {
			if timestamp > 1111110063 {
				return errors.New(errors.OSError, "")
			}
			return nil
		},
	}

	err := task.Execute()
	assert.Error(t, err)
}
func TestTask_Execute_error(t *testing.T) {
	ctrl := gomock.NewController(t)
	rw := mockfacade.NewMockCheckpointReaderWriter(ctrl)
	facade.MockCheckpointReaderWriter(rw)

	rw.EXPECT().GetCheckpoint(gomock.Any(), gomock.Any()).Return(int64(0), errors.New(errors.OSError, "")).AnyTimes()

	ts := time.Now().Unix() / 60 * 60

	task := Task{
		Name:        "test",
		start:       ts,
		interval:    time.Minute,
		immediately: true,
		nextTimestamp: func(ctx context.Context, checkpoint int64, start int64) int64 {
			return start + 60
		},
		ready: func(ctx context.Context, timestamp int64) bool {
			return true
		},
		execute: func(ctx context.Context, timestamp int64) error {
			return errors.New(errors.S3Error, "")
		},
	}

	err := task.Execute()
	assert.Error(t, err)
}

func TestTask_loop_error(t *testing.T) {
	ctrl := gomock.NewController(t)
	rw := mockfacade.NewMockCheckpointReaderWriter(ctrl)
	facade.MockCheckpointReaderWriter(rw)

	rw.EXPECT().GetCheckpoint(gomock.Any(), gomock.Any()).Return(int64(0), errors.New(errors.OSError, "")).AnyTimes()

	ts := time.Now().Unix() / 60 * 60

	task := Task{
		Name:        "test",
		start:       ts,
		interval:    time.Minute,
		immediately: true,
		nextTimestamp: func(ctx context.Context, checkpoint int64, start int64) int64 {
			return start + 60
		},
		ready: func(ctx context.Context, timestamp int64) bool {
			return true
		},
		execute: func(ctx context.Context, timestamp int64) error {
			return errors.New(errors.S3Error, "")
		},
	}

	err := task.loop(context.TODO(), ts)
	assert.Error(t, err)
}

func TestTask_run_error(t *testing.T) {
	ts := time.Now().Unix() / 60 * 60

	task := Task{
		Name:        "test",
		start:       ts,
		interval:    time.Minute,
		immediately: true,
		nextTimestamp: func(ctx context.Context, checkpoint int64, start int64) int64 {
			return start + 60
		},
		ready: func(ctx context.Context, timestamp int64) bool {
			return true
		},
		execute: func(ctx context.Context, timestamp int64) error {
			return errors.New(errors.S3Error, "")
		},
	}

	err := task.run(context.TODO(), ts)
	assert.Error(t, err)
}

func TestTask_handleError(t1 *testing.T) {
	task := Task{
		Name: "test",
	}
	task.handleError(nil)
	task.handleError(errors.New(errors.IOError, ""))
	task.handleError(goerr.New(""))
}
