package app

import (
	"errors"
	"fmt"
	"strings"
	"time"

	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/server"
	"github.com/tidbcloud/cluster-service-ng/domain/server/br"
	"k8s.io/apimachinery/pkg/api/resource"
)

const pointInTimeFormat = "2006-01-02 15:04:05.000"

func validateTimeFormat(timestr, layout string) bool {
	_, err := time.Parse(layout, timestr)
	return err == nil
}

func down2UpGetLightweightRestoreResponse(resp *br.GetLightweightRestoreOutput) *apiv1.LightweightRestore {
	lwr := &apiv1.LightweightRestore{
		Status:                        resp.Status,
		Message:                       resp.Message,
		DataRestoreDurationSeconds:    resp.DataRestoreDurationSeconds,
		DataRestoreBytes:              resp.DataRestoreBytes,
		DataRestoreStep:               resp.DataRestoreStep,
		DataRestoreProgressPercentage: resp.DataRestoreProgressPercentage,
	}
	return lwr
}

func down2UpListLightweightBackupsResponse(resp *server.ListLightweightBackupsOutput) *apiv1.ListLightweightBackupsResponse {
	out := &apiv1.ListLightweightBackupsResponse{
		DfsPrefix:   resp.DfsPrefix,
		DfsBucket:   resp.DfsBucket,
		DfsRegion:   resp.DfsRegion,
		PdClusterId: resp.ListLightweightBackupsResponse.ClusterID,
	}
	for _, it := range resp.ListLightweightBackupsResponse.Data {
		backup := &apiv1.LightweightBackup{
			Id:         it.ID,
			Name:       it.Name,
			BackupTime: it.LastModifyTime,
			BackupTs:   it.Details.BackupTs,
		}

		for _, keyspace := range it.Details.Keyspaces {
			if !keyspace.IsNormalKeyspace() {
				continue
			}
			backup.ClusterBackupInfo = append(backup.ClusterBackupInfo, &apiv1.ClusterBackupInfo{
				ClusterId:       resp.Keyspace2ClusterId[keyspace.KeyspaceName],
				BackupSizeBytes: keyspace.Size,
			})
		}
		out.Backups = append(out.Backups, backup)
	}
	out.TotalCount = uint32(len(out.Backups))
	return out
}

func validateCreateLightweightRestoreRequest(req *apiv1.CreateLightweightRestoreRequest) error {
	// validate restore mode
	err := validateRestoreMode(req)
	if err != nil {
		return err
	}

	err = validateRestoreResources(req)
	if err != nil {
		return err
	}

	return nil
}

func validateCreateClassicRestoreRequest(req *apiv1.CreateClassicRestoreRequest) error {
	if req.RestoreId == "" {
		return errors.New("restore_id is required")
	}

	if req.BackupStorage == "" {
		return errors.New("backup_storage is required")
	}

	if req.BackupDataSizeBytes == 0 {
		return errors.New("backup_data_size_bytes is required")
	}

	if req.BackupCloudProvider == "" {
		// FIXME: for backward compatibility, default to aws, after tidb-management-service ready, remove this
		req.BackupCloudProvider = "aws"
		// return errors.New("backup_cloud_provider is required")
	}

	if req.BackupCloudProvider != "aws" && req.BackupCloudProvider != "alicloud" {
		return errors.New("invalid backup_cloud_provider: " + req.BackupCloudProvider)
	}

	if req.BackupRegion == "" {
		return errors.New("backup_region is required")
	}

	if (req.AccessKeyId == "" && req.AccessKeySecret != "") || (req.AccessKeyId != "" && req.AccessKeySecret == "") {
		return errors.New("access_key_id and access_key_secret must be set together or not set together")
	}

	if req.TargetSharedPoolId == "" {
		return errors.New("target_shared_pool_id is required")
	}

	if req.TargetClusterId == "" {
		return errors.New("target_cluster_id is required")
	}

	err := validateRestoreResources(req)
	if err != nil {
		return err
	}

	return nil
}

func validateCreateManualBackupRequest(req *apiv1.CreateManualBackupRequest) error {
	if req.BackupId == "" {
		return errors.New("backup_id is required")
	}
	if req.ClusterId == "" {
		return errors.New("cluster_id is required")
	}
	return nil
}

func validateRestoreMode(req *apiv1.CreateLightweightRestoreRequest) error {
	isSnapshot := req.KernelBackupId != 0 || strings.TrimSpace(req.KernelBackupName) != ""
	isPiTR := strings.TrimSpace(req.PointInTime) != ""

	// Rule: mutually exclusive
	if isSnapshot && isPiTR {
		return errors.New("snapshot fields (kernel_backup_id, kernel_backup_name) and point_in_time cannot both be set")
	}

	// Rule: snapshot restore requires both backup_id and backup_name
	if isSnapshot {
		if req.KernelBackupId == 0 || strings.TrimSpace(req.KernelBackupName) == "" {
			return errors.New("both kernel_backup_id and kernel_backup_name must be set for snapshot restore")
		}
		return nil
	}

	// Rule: point_in_time must be non-empty for PiTR
	if isPiTR {
		if !validateTimeFormat(req.PointInTime, pointInTimeFormat) {
			return fmt.Errorf("invalid point_in_time format, expected %s", pointInTimeFormat)
		}
		return nil
	}

	// Rule: at least one type of restore must be specified
	if !isSnapshot && !isPiTR {
		return errors.New("either snapshot (kernel_backup_id and kernel_backup_name) or point_in_time must be specified")
	}

	return nil
}

type resourceRequest interface {
	GetResourceCpu() string
	GetResourceMemory() string
	GetResourceStorage() string
}

func validateRestoreResources(req resourceRequest) error {
	if req.GetResourceCpu() != "" {
		_, err := resource.ParseQuantity(req.GetResourceCpu())
		if err != nil {
			return fmt.Errorf("invalid ResourceCpu format: %s, expected format like '500m' or '1' (for 1 CPU core) or '2' (for 2 CPU cores)", err.Error())
		}
	}

	if req.GetResourceMemory() != "" {
		_, err := resource.ParseQuantity(req.GetResourceMemory())
		if err != nil {
			return fmt.Errorf("invalid ResourceMemory format: %s, expected format like '512Mi' or '1Gi'", err.Error())
		}
	}

	if req.GetResourceStorage() != "" {
		_, err := resource.ParseQuantity(req.GetResourceStorage())
		if err != nil {
			return fmt.Errorf("invalid ResourceStorage format: %s, expected format like '10Gi' or '100Mi'", err.Error())
		}
	}
	return nil
}

func validateTestClassicBackupAccessRequest(req *apiv1.TestClassicBackupAccessRequest) error {
	if req.CloudProvider == "" {
		return errors.New("cloud_provider is required")
	}

	if req.CloudProvider != "aws" && req.CloudProvider != "alicloud" {
		return errors.New("invalid cloud_provider: " + req.CloudProvider)
	}

	if req.BucketUri == "" {
		return errors.New("bucket_uri is required")
	}

	if req.AccessKeyId == "" {
		return errors.New("access_key_id is required")
	}

	if req.AccessKeySecret == "" {
		return errors.New("access_key_secret is required")
	}

	if req.CloudProvider == "alicloud" {
		if req.Region == "" {
			return errors.New("region is required")
		}
	}
	return nil
}
