package statistic

import (
	"context"

	"github.com/pingcap/log"
	"go.uber.org/zap"

	"github.com/tidbcloud/observability/o11ymetering/pkg/common/config"
	"github.com/tidbcloud/observability/o11ymetering/pkg/common/constants"
	"github.com/tidbcloud/observability/o11ymetering/pkg/common/message"
	"github.com/tidbcloud/observability/o11ymetering/pkg/common/models"
	"github.com/tidbcloud/observability/o11ymetering/pkg/facade"
	"github.com/tidbcloud/observability/o11ymetering/pkg/service/statistic/handler"
)

func GenerateStatisticTask(ctx context.Context, req message.GenerateStatisticTaskReq) (message.GenerateStatisticTaskResp, error) {
	var resp message.GenerateStatisticTaskResp
	taskBuilders := listTasksHandler()
	var tasks []*models.StatisticTask
	for _, taskBuilder := range taskBuilders {
		task, err := taskBuilder.Build(ctx, req.Period)
		if err != nil {
			// TODO Statistic handle error
			return resp, err
		} else {
			tasks = append(tasks, &task)
		}
	}

	tasks, err := facade.GetCurrentStatisticReaderWriter().BatchCreateTask(ctx, tasks)
	if err != nil {
		// TODO Statistic handle error
		return resp, err
	}
	var taskIDs = make([]string, 0, len(tasks))
	for _, task := range tasks {
		taskIDs = append(taskIDs, task.ID)
	}

	return message.GenerateStatisticTaskResp{
		TaskIDs: taskIDs,
	}, nil
}

var getTasks = config.GetTasks

func listTasksHandler() []handler.TaskHandler {
	var handlers = make([]handler.TaskHandler, 0, len(getTasks()))
	for _, t := range getTasks() {
		handlers = append(handlers, pickHandler(t.StatisticClass, t.MeteringGroup, t.Env))
	}
	return handlers
}

func ExecuteStatisticTask(ctx context.Context, req message.ExecuteStatisticTaskReq) (message.ExecuteStatisticTaskResp, error) {
	task, err := facade.GetCurrentStatisticReaderWriter().GetTask(ctx, req.ID)
	if err != nil {
		return message.ExecuteStatisticTaskResp{}, err
	}

	err = facade.GetCurrentStatisticReaderWriter().UpdateTaskState(ctx, task.ID, constants.StatePending, constants.StateProcessing)
	if err != nil {
		return message.ExecuteStatisticTaskResp{}, err
	}

	handler := pickHandler(task.StatisticClass, task.MeteringGroup, task.Env)
	records, err := handler.Execute(ctx, *task)
	if err != nil {
		if innerError := facade.GetCurrentStatisticReaderWriter().UpdateTaskState(ctx, task.ID, constants.StateProcessing, constants.StateFailed); innerError != nil {
			log.Error(innerError.Error())
		}
		return message.ExecuteStatisticTaskResp{}, err
	}
	log.Info("fetched records for task", zap.Int("records", len(records)), zap.Any("task", *task))

	records, err = facade.GetCurrentStatisticReaderWriter().SaveStatisticRecordsForTask(ctx, task.ID, records)
	if err != nil {
		return message.ExecuteStatisticTaskResp{}, err
	}

	return message.ExecuteStatisticTaskResp{
		// nolint:gosec
		RecordsCount: int32(len(records)),
	}, nil
}

var pickHandler = pickTaskHandler

func pickTaskHandler(statisticClass constants.StatisticClass, meteringGroup constants.MeteringGroup, bizEnv constants.BizEnv) handler.TaskHandler {
	if statisticClass == constants.MetricsCollection && meteringGroup == constants.DedicatedDataplane {
		return handler.DedicatedDataplaneMetricCollection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.MetricsCollection && meteringGroup == constants.Serverless {
		return handler.ServerlessMetricCollection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.MetricsCollection && meteringGroup == constants.CloudPlatform {
		return handler.CloudPlatformBaseMetricCollection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.MetricsCollection && meteringGroup == constants.DedicatedRegionalControl {
		return handler.DedicatedRegionalControlMetricCollection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.LogsCollectionLoki && meteringGroup == constants.DedicatedDataplane {
		return handler.DedicatedDataplaneLokiCollection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.LogsCollectionLoki && meteringGroup == constants.Serverless {
		return handler.ServerlessLokiCollection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.LogsCollectionLoki && meteringGroup == constants.CloudPlatform {
		return handler.CloudPlatformBaseLokiCollection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.LogsCollectionLoki && meteringGroup == constants.DedicatedRegionalControl {
		return handler.DedicatedRegionalControlLokiCollection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.LogsStorageS3 && meteringGroup == constants.DedicatedDataplane {
		return handler.DedicatedDataplaneS3Collection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.LogsStorageS3 && meteringGroup == constants.Serverless {
		return handler.ServerlessS3Collection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.LogsStorageS3 && meteringGroup == constants.CloudPlatform {
		return handler.CloudPlatformBaseS3Collection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.LogsStorageS3 && meteringGroup == constants.DedicatedRegionalControl {
		return handler.DedicatedRegionalControlS3Collection{
			Env: bizEnv,
		}
	} else if statisticClass == constants.DryRunStatisticClass && meteringGroup == constants.DryRunMeteringGroup {
		return handler.DryRun{
			Env: bizEnv,
		}
	} else {
		panic("implement me")
	}
}
