package cvt

import (
	"encoding/json"
	"fmt"

	ccent "github.com/tidbcloud/devops/entity/cloudconfig"
	"github.com/tidbcloud/devops/models/base"
	ccmod "github.com/tidbcloud/devops/models/cloudconfig"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
	timeutil "github.com/tidbcloud/devops/pkg/infra/utils/time"
)

type e2m struct{}

func (cvt e2m) ConfigurationChange(e *ccent.ConfigurationChange) *ccmod.ConfigurationChange {
	m := &ccmod.ConfigurationChange{
		Updatable: base.Updatable{
			Base: base.Base{ID: base.ID(e.ID)},
		},

		TargetType:     string(e.TargetType),
		TargetVersion:  e.TargetVersion,
		ChangeType:     e.ChangeType,
		Reason:         e.Reason,
		Applicant:      e.Applicant,
		OperatorEntity: e.OperatorEntity,
		ApplyPolicy:    string(e.ApplyPolicy),

		Phase:      string(e.Phase),
		ApprovalID: e.ApprovalID,
		ClosedAt:   timeutil.TsToTime(e.ClosedAt),
		AppliedAt:  timeutil.TsToTime(e.AppliedAt),

		ExecutionWindowID: e.ExecutionWindowID,

		PremiumUpgradeStepID: e.PremiumUpgradeStepID,
	}

	for _, file := range e.Scope.Files {
		m.Files = append(m.Files, cvt.ConfigurationFileChange(e.ID, file))
	}
	for _, comp := range e.Scope.Components {
		m.Components = append(m.Components, cvt.ConfigurationComponentChange(e.ID, comp))
	}
	if e.Scope.Version != nil {
		m.Version = cvt.ConfigurationVersionChange(e.ID, e.Scope.Version)
	}
	if e.TargetCluster != nil {
		m.ToCluster = &ccmod.ConfigurationChangeToCluster{
			ClusterType: string(e.TargetCluster.ClusterType),
			ClusterID:   e.TargetCluster.ClusterID,
			ChangeID:    e.ID,
		}
	}
	if e.TargetTenantInit != nil {
		m.ToTenantInit = &ccmod.ConfigurationChangeToTenantInit{
			ClusterType: string(e.TargetTenantInit.ClusterType),
			TenantID:    e.TargetTenantInit.TenantID,
			ChangeID:    e.ID,
		}
	}
	if e.TargetTemplate != nil {
		m.ToTemplate = &ccmod.ConfigurationChangeToTemplate{
			ClusterType:  string(e.TargetTemplate.ClusterType),
			TemplateType: string(e.TargetTemplate.TemplateType),
			TenantID:     e.TargetTemplate.TenantID,
			ChangeID:     e.ID,
		}
	}

	return m
}

func (cvt e2m) ConfigurationFileChange(chgID uint64, e *ccent.ConfigurationFileChange) *ccmod.ConfigurationFileChange {
	var err error

	m := &ccmod.ConfigurationFileChange{
		Base: base.Base{ID: base.ID(e.ID)},

		Filename:          e.Filename,
		ConfigType:        e.ConfigType,
		TemplateID:        e.TemplateID,
		PreviousText:      e.PreviousText,
		ClearCustomParams: e.ClearCustomParams,
		ChangeID:          chgID,
	}

	m.AllParameters, err = json.Marshal(e.AllParameters)
	if err != nil {
		panic(fmt.Errorf("failed to marshal custom configs: %v", err))
	}

	if len(e.ModifiedParameters) != 0 {
		data, err := json.Marshal(e.ModifiedParameters)
		if err != nil {
			panic(fmt.Errorf("failed to marshal modified configs: %v", err))
		}
		m.ModifiedParameters = data
	}

	if e.ModifiedTemplate != nil {
		data, err := json.Marshal(e.ModifiedTemplate)
		if err != nil {
			panic(fmt.Errorf("failed to marshal modified template: %v", err))
		}
		m.ModifiedTemplate = data
	}

	return m
}

func (cvt e2m) ConfigurationComponentChange(chgID uint64, e *ccent.ConfigurationComponentChange) *ccmod.ConfigurationComponentChange {
	var err error

	m := &ccmod.ConfigurationComponentChange{
		Base: base.Base{ID: base.ID(e.ID)},

		Component: e.Component,
		ChangeID:  chgID,
	}

	if e.ModifiedImageTag != nil {
		m.ModifiedImageTag, err = json.Marshal(e.ModifiedImageTag)
		if err != nil {
			panic(fmt.Errorf("failed to marshal modified image tag: %v", err))
		}
	}

	if e.ModifiedVersion != nil {
		m.ModifiedVersion, err = json.Marshal(e.ModifiedVersion)
		if err != nil {
			panic(fmt.Errorf("failed to marshal modified version: %v", err))
		}
	}

	if e.ModifiedOverwriteImageTag != nil {
		m.ModifiedOverwriteImageTag, err = json.Marshal(e.ModifiedOverwriteImageTag)
		if err != nil {
			panic(fmt.Errorf("failed to marshal modified overwrite image tag: %v", err))
		}
	}

	return m
}

func (cvt e2m) ConfigurationVersionChange(chgID uint64, e *ccent.ConfigurationVersionChange) *ccmod.ConfigurationVersionChange {
	m := &ccmod.ConfigurationVersionChange{
		Base:        base.Base{ID: base.ID(e.ID)},
		FromVersion: e.From,
		ToVersion:   e.To,
		ChangeID:    chgID,
	}
	return m
}

func (cvt e2m) DefaultConfigurationChange(e *ccent.DefaultConfigurationChange) *ccmod.DefaultConfigurationChange {
	m := &ccmod.DefaultConfigurationChange{
		Updatable: base.Updatable{
			Base: base.Base{ID: base.ID(e.ID)},
		},
		ClosedAt:     timeutil.TsToTime(e.ClosedAt),
		Applicant:    e.Applicant,
		Reason:       e.Reason,
		DisplayName:  e.DisplayName,
		Phase:        string(e.Phase),
		PhaseReason:  e.PhaseReason,
		ItemTypes:    container.TransformSlice(e.ItemTypes, container.ToString),
		Environments: e.Environments,
	}

	return m
}

func (cvt e2m) DefaultConfigurationChangeRelease(e *ccent.DefaultConfigurationChangeRelease) *ccmod.DefaultConfigurationChangeRelease {
	m := &ccmod.DefaultConfigurationChangeRelease{
		ID:            base.ID(e.ID),
		Environment:   e.Environment,
		Description:   e.Description,
		ReleasedAt:    timeutil.TsToTime(e.ReleasedAt),
		ReleaseNumber: e.ReleaseNumber,
		Phase:         string(e.Phase),
		ApprovalID:    e.ApprovalID,
		ParentID:      e.ParentID,
	}

	return m
}

func (cvt e2m) DefaultConfigurationChangeItem(e *ccent.DefaultConfigurationChangeItem) *ccmod.DefaultConfigurationChangeItem {
	m := &ccmod.DefaultConfigurationChangeItem{
		ID:       base.ID(e.ID),
		ParentID: e.ParentID,
		ItemType: string(e.ItemType),
	}

	if e.Data != nil {
		var err error
		m.Data, err = json.Marshal(e.Data)
		if err != nil {
			panic(fmt.Errorf("failed to marshal data: %v", err))
		}
	}

	return m
}

func (cvt e2m) DefaultConfigurationChangeItemRelease(e *ccent.DefaultConfigurationChangeItemRelease) *ccmod.DefaultConfigurationChangeItemRelease {
	m := &ccmod.DefaultConfigurationChangeItemRelease{
		ID:            base.ID(e.ID),
		Environment:   e.Environment,
		ItemID:        e.ItemID,
		ReleaseNumber: e.ReleaseNumber,
		ParentID:      e.ParentID,
	}

	if e.Data != nil {
		var err error
		m.Data, err = json.Marshal(e.Data)
		if err != nil {
			panic(fmt.Errorf("failed to marshal data: %v", err))
		}
	}

	return m
}
