package impl

import "github.com/tidbcloud/dataflow-service/app/repo/entity"

// cloneReplayForExecution deep-copies replay fields that request preparation may
// decrypt or rewrite, so the stored entity keeps its encrypted credential values.
func cloneReplayForExecution(replayEnt *entity.Replay) *entity.Replay {
	if replayEnt == nil {
		return nil
	}
	cloned := *replayEnt
	cloned.SourceAuditCredential = clonePtrWith(replayEnt.SourceAuditCredential, cloneAuditLogAddr)
	cloned.ReplayTasks = cloneSlice(replayEnt.ReplayTasks, cloneReplayTask)
	return &cloned
}

func cloneReplayTask(task entity.ReplayTask) entity.ReplayTask {
	cloned := task
	cloned.DataSource.SourceAuditLogAddrs = cloneSlice(task.DataSource.SourceAuditLogAddrs, cloneAuditLogAddr)
	cloned.DataTarget.Username = clonePtr(task.DataTarget.Username)
	cloned.DataTarget.Password = clonePtr(task.DataTarget.Password)
	cloned.Resources = clonePtr(task.Resources)
	cloned.PerReplayConfigs = cloneSlice(task.PerReplayConfigs, cloneReplayConfig)
	return cloned
}

func cloneAuditLogAddr(addr entity.AuditLogAddr) entity.AuditLogAddr {
	cloned := addr
	cloned.Aliyun = clonePtr(addr.Aliyun)
	cloned.AWS = clonePtrWith(addr.AWS, cloneAWSAuditLog)
	cloned.GCP = clonePtrWith(addr.GCP, cloneGCPAuditLog)
	return cloned
}

func cloneAWSAuditLog(auditLog entity.AWSAuditLog) entity.AWSAuditLog {
	cloned := auditLog
	cloned.ExternalID = clonePtr(auditLog.ExternalID)
	return cloned
}

func cloneGCPAuditLog(auditLog entity.GCPAuditLog) entity.GCPAuditLog {
	cloned := auditLog
	cloned.ServiceAccountJSON = clonePtr(auditLog.ServiceAccountJSON)
	return cloned
}

func cloneReplayConfig(cfg entity.ReplayConfig) entity.ReplayConfig {
	cloned := cfg
	cloned.ReplayStartTime = clonePtr(cfg.ReplayStartTime)
	cloned.RunStartTime = clonePtr(cfg.RunStartTime)
	cloned.ReplayIndex = clonePtr(cfg.ReplayIndex)
	cloned.Format = clonePtr(cfg.Format)
	cloned.IgnoreErrs = clonePtr(cfg.IgnoreErrs)
	cloned.PsClose = clonePtr(cfg.PsClose)
	cloned.BufSize = clonePtr(cfg.BufSize)
	cloned.Speed = clonePtr(cfg.Speed)
	cloned.DynamicInput = clonePtr(cfg.DynamicInput)
	cloned.ReadOnly = clonePtr(cfg.ReadOnly)
	cloned.CustomArguments = cloneMap(cfg.CustomArguments)
	return cloned
}

func clonePtr[T any](value *T) *T {
	return clonePtrWith(value, func(value T) T { return value })
}

func clonePtrWith[T any](value *T, clone func(T) T) *T {
	if value == nil {
		return nil
	}
	cloned := clone(*value)
	return &cloned
}

func cloneSlice[S ~[]E, E any](values S, clone func(E) E) S {
	if values == nil {
		return nil
	}
	cloned := make(S, len(values))
	for i := range values {
		cloned[i] = clone(values[i])
	}
	return cloned
}

func cloneMap[M ~map[K]V, K comparable, V any](values M) M {
	if values == nil {
		return nil
	}
	cloned := make(M, len(values))
	for k, v := range values {
		cloned[k] = v
	}
	return cloned
}
