package case_driver

import (
	"fmt"
	"strings"

	"github.com/elliotchance/pie/v2"
	"github.com/tidbcloud/dataflow-service-e2e/cases"
	"github.com/tidbcloud/dataflow-service-e2e/cases/essential/backup"
	"github.com/tidbcloud/dataflow-service-e2e/cases/essential/changefeed"
	dmcase "github.com/tidbcloud/dataflow-service-e2e/cases/essential/dm"
	exportcase "github.com/tidbcloud/dataflow-service-e2e/cases/essential/export"
	importcase "github.com/tidbcloud/dataflow-service-e2e/cases/essential/import_data"
	"github.com/tidbcloud/dataflow-service-e2e/internal/config"
	. "github.com/tidbcloud/dataflow-service-e2e/internal/model"
)

// RegisterEssentialCases installs the explicit Essential v1 matrix. Snapshot
// restore is implemented but intentionally unregistered because the automatic
// backup fixture is not suitable for the active suite.
func RegisterEssentialCases() {
	AllCases = map[string]cases.E2ECase{}

	// Export cases.
	awsExportLifecycleSmokeCase := exportcase.NewExportLifecycleSmokeCase(AWS)
	alicloudExportLifecycleSmokeCase := exportcase.NewExportLifecycleSmokeCase(AliCloud)
	awsExportCancellationSmokeCase := exportcase.NewExportCancellationSmokeCase(AWS)
	alicloudExportCancellationSmokeCase := exportcase.NewExportCancellationSmokeCase(AliCloud)
	awsExportInvalidRequestSmokeCase := exportcase.NewExportInvalidRequestSmokeCase(AWS)
	alicloudExportInvalidRequestSmokeCase := exportcase.NewExportInvalidRequestSmokeCase(AliCloud)
	addCases(
		awsExportLifecycleSmokeCase,
		alicloudExportLifecycleSmokeCase,
		awsExportCancellationSmokeCase,
		alicloudExportCancellationSmokeCase,
		awsExportInvalidRequestSmokeCase,
		alicloudExportInvalidRequestSmokeCase,
	)

	// Backup and restore cases.
	awsPiTRRestoreSmokeCase := backup.NewPiTRRestoreSmokeCase(AWS)
	alicloudPiTRRestoreSmokeCase := backup.NewPiTRRestoreSmokeCase(AliCloud)
	awsBackupRetentionListSmokeCase := backup.NewBackupRetentionListSmokeCase(AWS)
	alicloudBackupRetentionListSmokeCase := backup.NewBackupRetentionListSmokeCase(AliCloud)
	addCases(
		awsPiTRRestoreSmokeCase,
		alicloudPiTRRestoreSmokeCase,
		awsBackupRetentionListSmokeCase,
		alicloudBackupRetentionListSmokeCase,
	)

	// CDC cases.
	awsCDCChangefeedLifecycleSmokeCase := changefeed.NewCDCChangefeedLifecycleSmokeCase(AWS)
	alicloudCDCChangefeedLifecycleSmokeCase := changefeed.NewCDCChangefeedLifecycleSmokeCase(AliCloud)
	awsCDCMySQLLifecycleCase := changefeed.NewCDCMySQLLifecycleCase(AWS)
	alicloudCDCMySQLLifecycleCase := changefeed.NewCDCMySQLLifecycleCase(AliCloud)
	awsCDCKafkaConfigRecreateCase := changefeed.NewCDCKafkaConfigRecreateCase(AWS)
	alicloudCDCKafkaConfigRecreateCase := changefeed.NewCDCKafkaConfigRecreateCase(AliCloud)
	awsCDCScaleCase := changefeed.NewCDCScaleCase(AWS)
	alicloudCDCScaleCase := changefeed.NewCDCScaleCase(AliCloud)
	awsCDCInvalidParamsCase := changefeed.NewCDCInvalidParamsCase(AWS)
	alicloudCDCInvalidParamsCase := changefeed.NewCDCInvalidParamsCase(AliCloud)
	awsCDCKafkaPrivateLinkCase := changefeed.NewCDCKafkaPrivateLinkCase(AWS)
	alicloudCDCKafkaPrivateLinkCase := changefeed.NewCDCKafkaPrivateLinkCase(AliCloud)
	awsCDCMySQLPrivateLinkCase := changefeed.NewCDCMySQLPrivateLinkCase(AWS)
	alicloudCDCMySQLPrivateLinkCase := changefeed.NewCDCMySQLPrivateLinkCase(AliCloud)
	addCases(
		awsCDCChangefeedLifecycleSmokeCase,
		alicloudCDCChangefeedLifecycleSmokeCase,
		awsCDCMySQLLifecycleCase,
		alicloudCDCMySQLLifecycleCase,
		awsCDCKafkaConfigRecreateCase,
		alicloudCDCKafkaConfigRecreateCase,
		awsCDCScaleCase,
		alicloudCDCScaleCase,
		awsCDCInvalidParamsCase,
		alicloudCDCInvalidParamsCase,
		awsCDCKafkaPrivateLinkCase,
		alicloudCDCKafkaPrivateLinkCase,
		awsCDCMySQLPrivateLinkCase,
		alicloudCDCMySQLPrivateLinkCase,
	)

	// DM cases.
	awsDMFullMigrationLifecycleSmokeCase := dmcase.NewFullMigrationLifecycleSmokeCase(AWS)
	alicloudDMFullMigrationLifecycleSmokeCase := dmcase.NewFullMigrationLifecycleSmokeCase(AliCloud)
	awsDMIncrementalLifecycleCase := dmcase.NewDMIncrementalLifecycleCase(AWS)
	alicloudDMIncrementalLifecycleCase := dmcase.NewDMIncrementalLifecycleCase(AliCloud)
	awsDMPrecheckLifecycleCase := dmcase.NewDMPrecheckLifecycleCase(AWS)
	alicloudDMPrecheckLifecycleCase := dmcase.NewDMPrecheckLifecycleCase(AliCloud)
	awsDMInvalidPrecheckCase := dmcase.NewDMInvalidPrecheckCase(AWS)
	alicloudDMInvalidPrecheckCase := dmcase.NewDMInvalidPrecheckCase(AliCloud)
	awsDMPrivateLinkCase := dmcase.NewDMPrivateLinkCase(AWS)
	alicloudDMPrivateLinkCase := dmcase.NewDMPrivateLinkCase(AliCloud)
	addCases(
		awsDMFullMigrationLifecycleSmokeCase,
		alicloudDMFullMigrationLifecycleSmokeCase,
		awsDMIncrementalLifecycleCase,
		alicloudDMIncrementalLifecycleCase,
		awsDMPrecheckLifecycleCase,
		alicloudDMPrecheckLifecycleCase,
		awsDMInvalidPrecheckCase,
		alicloudDMInvalidPrecheckCase,
		awsDMPrivateLinkCase,
		alicloudDMPrivateLinkCase,
	)

	// Import cases.
	awsImportLifecycleSmokeCase := importcase.NewImportLifecycleSmokeCase(AWS)
	alicloudImportLifecycleSmokeCase := importcase.NewImportLifecycleSmokeCase(AliCloud)
	awsImportCSVLifecycleSmokeCase := importcase.NewImportCSVLifecycleSmokeCase(AWS)
	alicloudImportCSVLifecycleSmokeCase := importcase.NewImportCSVLifecycleSmokeCase(AliCloud)
	awsImportCancellationSmokeCase := importcase.NewImportCancellationSmokeCase(AWS)
	alicloudImportCancellationSmokeCase := importcase.NewImportCancellationSmokeCase(AliCloud)
	awsImportInvalidSourceSmokeCase := importcase.NewImportInvalidSourceSmokeCase(AWS)
	alicloudImportInvalidSourceSmokeCase := importcase.NewImportInvalidSourceSmokeCase(AliCloud)
	addCases(
		awsImportLifecycleSmokeCase,
		alicloudImportLifecycleSmokeCase,
		awsImportCSVLifecycleSmokeCase,
		alicloudImportCSVLifecycleSmokeCase,
		awsImportCancellationSmokeCase,
		alicloudImportCancellationSmokeCase,
		awsImportInvalidSourceSmokeCase,
		alicloudImportInvalidSourceSmokeCase,
	)

	// Import data-format cases (AURORA_SNAPSHOT/PARQUET). These run only when
	// selected by name or through the automatic "all" group; they are
	// intentionally not part of the smoke/weekly groups.
	awsImportAuroraSnapshotLifecycleCase := importcase.NewImportDataFormatLifecycleCase(AWS, config.DataFormatAuroraSnapshot)
	awsImportParquetLifecycleCase := importcase.NewImportDataFormatLifecycleCase(AWS, config.DataFormatParquet)
	addCases(awsImportAuroraSnapshotLifecycleCase, awsImportParquetLifecycleCase)

	// Group cases. As in the Premium registry, aggregate groups are assembled
	// automatically and provider-specific scenario groups are listed manually.
	CaseGroups = map[string][]cases.E2ECase{
		// Assembled automatically below.
		"all":              {},
		"awsAll":           {},
		"alicloudAll":      {},
		"smoke":            {},
		"consoleSmoke":     {},
		"weeklyRegression": {},

		// Manually maintained scenario groups.
		"awsSmoke": {
			awsExportLifecycleSmokeCase,
			awsPiTRRestoreSmokeCase,
			awsCDCChangefeedLifecycleSmokeCase,
			awsDMFullMigrationLifecycleSmokeCase,
			awsImportLifecycleSmokeCase,
		},
		"alicloudSmoke": {
			alicloudExportLifecycleSmokeCase,
			alicloudPiTRRestoreSmokeCase,
			alicloudCDCChangefeedLifecycleSmokeCase,
			alicloudDMFullMigrationLifecycleSmokeCase,
			alicloudImportLifecycleSmokeCase,
		},
		"openapiSmoke": {
			awsExportLifecycleSmokeCase,
			awsCDCChangefeedLifecycleSmokeCase,
			awsDMFullMigrationLifecycleSmokeCase,
			awsImportLifecycleSmokeCase,
			alicloudExportLifecycleSmokeCase,
			alicloudCDCChangefeedLifecycleSmokeCase,
			alicloudDMFullMigrationLifecycleSmokeCase,
			alicloudImportLifecycleSmokeCase,
		},
		"awsWeeklyRegression": {
			awsExportLifecycleSmokeCase,
			awsExportCancellationSmokeCase,
			awsExportInvalidRequestSmokeCase,
			awsPiTRRestoreSmokeCase,
			awsBackupRetentionListSmokeCase,
			awsCDCChangefeedLifecycleSmokeCase,
			awsCDCMySQLLifecycleCase,
			awsCDCKafkaConfigRecreateCase,
			awsCDCScaleCase,
			awsCDCInvalidParamsCase,
			// awsCDCKafkaPrivateLinkCase,
			// awsCDCMySQLPrivateLinkCase,
			awsDMFullMigrationLifecycleSmokeCase,
			awsDMIncrementalLifecycleCase,
			awsDMPrecheckLifecycleCase,
			awsDMInvalidPrecheckCase,
			// awsDMPrivateLinkCase,
			awsImportLifecycleSmokeCase,
			awsImportCSVLifecycleSmokeCase,
			awsImportCancellationSmokeCase,
			awsImportInvalidSourceSmokeCase,
		},
		"alicloudWeeklyRegression": {
			alicloudExportLifecycleSmokeCase,
			alicloudExportCancellationSmokeCase,
			alicloudExportInvalidRequestSmokeCase,
			alicloudPiTRRestoreSmokeCase,
			alicloudBackupRetentionListSmokeCase,
			alicloudCDCChangefeedLifecycleSmokeCase,
			alicloudCDCMySQLLifecycleCase,
			alicloudCDCKafkaConfigRecreateCase,
			alicloudCDCScaleCase,
			alicloudCDCInvalidParamsCase,
			alicloudCDCKafkaPrivateLinkCase,
			alicloudCDCMySQLPrivateLinkCase,
			alicloudDMFullMigrationLifecycleSmokeCase,
			alicloudDMIncrementalLifecycleCase,
			alicloudDMPrecheckLifecycleCase,
			alicloudDMInvalidPrecheckCase,
			alicloudDMPrivateLinkCase,
			alicloudImportLifecycleSmokeCase,
			alicloudImportCSVLifecycleSmokeCase,
			alicloudImportCancellationSmokeCase,
			alicloudImportInvalidSourceSmokeCase,
		},
		"awsNewRegion": {
			awsExportLifecycleSmokeCase,
			awsPiTRRestoreSmokeCase,
			awsBackupRetentionListSmokeCase,
			awsCDCChangefeedLifecycleSmokeCase,
			awsCDCMySQLLifecycleCase,
			awsCDCKafkaConfigRecreateCase,
			awsCDCScaleCase,
			awsCDCKafkaPrivateLinkCase,
			awsCDCMySQLPrivateLinkCase,
			awsDMFullMigrationLifecycleSmokeCase,
			awsDMIncrementalLifecycleCase,
			awsDMPrecheckLifecycleCase,
			awsDMPrivateLinkCase,
			awsImportLifecycleSmokeCase,
			awsImportCSVLifecycleSmokeCase,
		},
		"alicloudNewRegion": {
			alicloudExportLifecycleSmokeCase,
			alicloudPiTRRestoreSmokeCase,
			alicloudBackupRetentionListSmokeCase,
			alicloudCDCChangefeedLifecycleSmokeCase,
			alicloudCDCMySQLLifecycleCase,
			alicloudCDCKafkaConfigRecreateCase,
			alicloudCDCScaleCase,
			alicloudCDCKafkaPrivateLinkCase,
			alicloudCDCMySQLPrivateLinkCase,
			alicloudDMFullMigrationLifecycleSmokeCase,
			alicloudDMIncrementalLifecycleCase,
			alicloudDMPrecheckLifecycleCase,
			alicloudDMPrivateLinkCase,
			alicloudImportLifecycleSmokeCase,
			alicloudImportCSVLifecycleSmokeCase,
		},
		"privateLink": {
			awsCDCKafkaPrivateLinkCase,
			awsCDCMySQLPrivateLinkCase,
			awsDMPrivateLinkCase,
			alicloudCDCKafkaPrivateLinkCase,
			alicloudCDCMySQLPrivateLinkCase,
			alicloudDMPrivateLinkCase,
		},
		"negative": {
			awsCDCInvalidParamsCase,
			awsDMInvalidPrecheckCase,
			awsExportInvalidRequestSmokeCase,
			awsImportInvalidSourceSmokeCase,
			alicloudCDCInvalidParamsCase,
			alicloudDMInvalidPrecheckCase,
			alicloudExportInvalidRequestSmokeCase,
			alicloudImportInvalidSourceSmokeCase,
		},
		"cancellation": {
			awsExportCancellationSmokeCase,
			awsImportCancellationSmokeCase,
			alicloudExportCancellationSmokeCase,
			alicloudImportCancellationSmokeCase,
		},
	}

	// Assemble automatic groups from the registered cases, using the same
	// dependency-driven provider classification as the Premium registry.
	for _, c := range AllCases {
		CaseGroups["all"] = append(CaseGroups["all"], c)
		reqs := pie.Filter(pie.Values(c.DeclareDependencyRequirements()), func(it DependencyRequirement) bool {
			return it.Type() == TiDBServerless
		})
		if pie.All(reqs, func(value DependencyRequirement) bool {
			return value.(*TiDBServerlessRequirement).CloudProvider == AWS
		}) {
			CaseGroups["awsAll"] = append(CaseGroups["awsAll"], c)
		}
		if pie.All(reqs, func(value DependencyRequirement) bool {
			return value.(*TiDBServerlessRequirement).CloudProvider == AliCloud
		}) {
			CaseGroups["alicloudAll"] = append(CaseGroups["alicloudAll"], c)
		}
	}
	CaseGroups["smoke"] = append(CaseGroups["smoke"], CaseGroups["awsSmoke"]...)
	CaseGroups["smoke"] = append(CaseGroups["smoke"], CaseGroups["alicloudSmoke"]...)
	CaseGroups["consoleSmoke"] = append(CaseGroups["consoleSmoke"], CaseGroups["smoke"]...)
	CaseGroups["weeklyRegression"] = append(CaseGroups["weeklyRegression"], CaseGroups["awsWeeklyRegression"]...)
	CaseGroups["weeklyRegression"] = append(CaseGroups["weeklyRegression"], CaseGroups["alicloudWeeklyRegression"]...)

	if config.GlobalConfig == nil {
		return
	}
	filterEssentialCasesByAPIMode()
	filterCasesByDomain()
	printAllCasesName()
	printCaseGroups()
}

func filterEssentialCasesByAPIMode() {
	mode := config.GlobalConfig.ApiMode
	if mode == "" {
		mode = config.ApiModeConsoleAPI
	}
	if err := ValidateEssentialAPIMode(mode); err != nil {
		panic(err)
	}
	for name, c := range AllCases {
		if err := ValidateEssentialCaseAPIMode(c, mode); err == nil {
			continue
		}
		for _, requested := range config.GlobalConfig.Cases2Run {
			if requested == name {
				panic(fmt.Sprintf("Essential case %q does not support API mode %q", name, mode))
			}
		}
		delete(AllCases, name)
	}
	for group, groupCases := range CaseGroups {
		filtered := make([]cases.E2ECase, 0, len(groupCases))
		for _, c := range groupCases {
			if supportsEssentialMode(c, mode) {
				filtered = append(filtered, c)
			}
		}
		CaseGroups[group] = filtered
	}
}

func supportsEssentialMode(c cases.E2ECase, mode string) bool {
	return ValidateEssentialCaseAPIMode(c, mode) == nil
}

func ValidateEssentialCaseAPIMode(c cases.E2ECase, mode string) error {
	if err := ValidateEssentialAPIMode(mode); err != nil {
		return err
	}
	for _, supported := range strings.Split(c.Tags()[cases.TagKeyAPIModes], ",") {
		if strings.TrimSpace(supported) == mode {
			return nil
		}
	}
	return fmt.Errorf("Essential case %q does not support API mode %q", c.Name(), mode)
}

func ValidateEssentialAPIMode(mode string) error {
	if mode != config.ApiModeConsoleAPI && mode != config.ApiModeOpenAPI {
		return fmt.Errorf("unsupported Essential API mode %q", mode)
	}
	return nil
}
