package domain

import (
	"time"

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

// Data Pipeline enum and persisted-plan aliases keep planners independent of
// transport protobufs while reusing the repository's exact durable values.
type (
	DataPipelineStatus            = entity.DataPipelineStatus
	DataPipelineSyncMode          = entity.DataPipelineSyncMode
	DataPipelineDestinationType   = entity.DataPipelineDestinationType
	DataPipelineStagingType       = entity.DataPipelineStagingType
	DataPipelineSnapshotEngine    = entity.DataPipelineSnapshotEngine
	DataPipelineSnapshotFormat    = entity.DataPipelineSnapshotFormat
	DataPipelineIncrementalEngine = entity.DataPipelineIncrementalEngine
	DataPipelineIncrementalFormat = entity.DataPipelineIncrementalFormat

	DataPipelineParams            = entity.DataPipelineParams
	DataPipelineSourceConfig      = entity.DataPipelineSourceConfig
	DataPipelineStagingConfig     = entity.DataPipelineStagingConfig
	DataPipelineS3Config          = entity.DataPipelineS3Config
	DataPipelineDestinationConfig = entity.DataPipelineDestinationConfig
	DataPipelineTargetNaming      = entity.DataPipelineTargetNaming

	DataPipelineExecutionPlan   = entity.DataPipelineExecutionPlan
	DataPipelineSnapshotPlan    = entity.DataPipelineSnapshotPlan
	DataPipelineIncrementalPlan = entity.DataPipelineIncrementalPlan
	DeliveryIntervalPlan        = entity.DeliveryIntervalPlan

	DataPipelineS3AuthType     = entity.DataPipelineS3AuthType
	DataPipelineS3Provider     = entity.DataPipelineS3Provider
	DataPipelineChildResources = entity.DataPipelineChildResources
	DataPipelineError          = entity.DataPipelineError
	DataPipelineObservation    = entity.DataPipelineObservation
)

const (
	DataPipelineS3AuthTypeAccessKey = entity.DataPipelineS3AuthTypeAccessKey
	DataPipelineS3AuthTypeRoleARN   = entity.DataPipelineS3AuthTypeRoleARN

	DataPipelineS3ProviderAWS        = entity.DataPipelineS3ProviderAWS
	DataPipelineS3ProviderAlibabaOSS = entity.DataPipelineS3ProviderAlibabaOSS

	DataPipelineStatusCreating  = entity.DataPipelineStatusCreating
	DataPipelineStatusRunning   = entity.DataPipelineStatusRunning
	DataPipelineStatusPausing   = entity.DataPipelineStatusPausing
	DataPipelineStatusPaused    = entity.DataPipelineStatusPaused
	DataPipelineStatusEditing   = entity.DataPipelineStatusEditing
	DataPipelineStatusResuming  = entity.DataPipelineStatusResuming
	DataPipelineStatusDeleting  = entity.DataPipelineStatusDeleting
	DataPipelineStatusFailed    = entity.DataPipelineStatusFailed
	DataPipelineStatusCompleted = entity.DataPipelineStatusCompleted
)

const (
	DataPipelineSyncModeFull               = entity.DataPipelineSyncModeFull
	DataPipelineSyncModeFullAndIncremental = entity.DataPipelineSyncModeFullAndIncremental

	DataPipelineDestinationTypeTiDBLake  = entity.DataPipelineDestinationTypeTiDBLake
	DataPipelineDestinationTypeSnowflake = entity.DataPipelineDestinationTypeSnowflake

	DataPipelineStagingTypeCustomerManagedS3 = entity.DataPipelineStagingTypeCustomerManagedS3
	DataPipelineStagingTypeTiDBManaged       = entity.DataPipelineStagingTypeTiDBManaged

	DataPipelineSnapshotEngineDumpling = entity.DataPipelineSnapshotEngineDumpling
	DataPipelineSnapshotEngineBR       = entity.DataPipelineSnapshotEngineBR
	DataPipelineSnapshotFormatCSV      = entity.DataPipelineSnapshotFormatCSV

	DataPipelineIncrementalEngineTiCDC     = entity.DataPipelineIncrementalEngineTiCDC
	DataPipelineIncrementalFormatCanalJSON = entity.DataPipelineIncrementalFormatCanalJSON
)

// DataPipeline is the business view used by models. It intentionally remains
// transport-agnostic; public API conversion must redact credential ciphertext.
type DataPipeline struct {
	ID                      uint64
	TenantID                uint64
	ClusterID               uint64
	CreatorID               uint64
	Creator                 string
	Name                    string
	Status                  DataPipelineStatus
	ChangefeedSpecification string
	Params                  DataPipelineParams
	ExecutionPlan           DataPipelineExecutionPlan
	IntervalPlan            DeliveryIntervalPlan
	SnapshotTSO             uint64
	StagingRoot             string
	Children                DataPipelineChildResources
	LastError               *DataPipelineError
	Observation             DataPipelineObservation
	CreatedAt               time.Time
	UpdatedAt               *time.Time
	DeletedAt               *time.Time
	Version                 int
}

type DataPipelineStagingPaths struct {
	Root        string
	Snapshot    string
	Incremental string
}

func DataPipelineEntity2Domain(p *entity.DataPipeline) *DataPipeline {
	if p == nil {
		return nil
	}

	result := &DataPipeline{
		ID:                      p.ID.Uint64(),
		TenantID:                p.TenantID.Uint64(),
		ClusterID:               p.ClusterID.Uint64(),
		CreatorID:               p.CreatorID.Uint64(),
		Creator:                 p.Creator,
		Name:                    p.Name,
		Status:                  p.Status,
		ChangefeedSpecification: p.ChangefeedSpecification,
		Params:                  cloneDataPipelineParams(p.Params),
		ExecutionPlan:           cloneDataPipelineExecutionPlan(p.ExecutionPlan),
		IntervalPlan:            p.IntervalPlan,
		SnapshotTSO:             p.SnapshotTSO,
		StagingRoot:             p.StagingRoot,
		Children:                p.Children,
		LastError:               cloneDataPipelineError(p.LastError),
		Observation:             cloneDataPipelineObservation(p.Observation),
		CreatedAt:               p.CreatedAt,
		UpdatedAt:               cloneTime(p.UpdatedAt),
		Version:                 p.Versioning,
	}
	if p.DeletedAt.Valid {
		result.DeletedAt = cloneTime(&p.DeletedAt.Time)
	}
	return result
}

func cloneDataPipelineParams(params DataPipelineParams) DataPipelineParams {
	result := params
	result.Source.TableFilter = append([]string(nil), params.Source.TableFilter...)
	return result
}

func cloneDataPipelineExecutionPlan(plan DataPipelineExecutionPlan) DataPipelineExecutionPlan {
	result := plan
	if plan.Incremental != nil {
		incremental := *plan.Incremental
		result.Incremental = &incremental
	}
	return result
}

func cloneDataPipelineObservation(observation DataPipelineObservation) DataPipelineObservation {
	result := observation
	result.LastAttemptAt = cloneTime(observation.LastAttemptAt)
	result.LastSucceededAt = cloneTime(observation.LastSucceededAt)
	result.ChangefeedCheckpointAt = cloneTime(observation.ChangefeedCheckpointAt)
	result.LakeResolvedAt = cloneTime(observation.LakeResolvedAt)
	result.PollError = cloneDataPipelineError(observation.PollError)
	return result
}

func cloneDataPipelineError(pipelineErr *DataPipelineError) *DataPipelineError {
	if pipelineErr == nil {
		return nil
	}
	result := *pipelineErr
	result.OccurredAt = cloneTime(pipelineErr.OccurredAt)
	return &result
}

func cloneTime(t *time.Time) *time.Time {
	if t == nil {
		return nil
	}
	result := *t
	return &result
}
