package cvt

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	gitent "github.com/tidbcloud/devops/entity/github"
	relent "github.com/tidbcloud/devops/entity/release"
	relmod "github.com/tidbcloud/devops/models/release"
	contutil "github.com/tidbcloud/devops/pkg/infra/utils/container"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"
)

type m2e struct{}

func (cvt m2e) Change(m *relmod.Change) *relent.ChangeInRepo {
	e := &relent.ChangeInRepo{
		ID: m.ID.Uint64(),

		Name:            m.Name,
		WindowStartAt:   m.WindowStartAt.Unix(),
		WindowEndAt:     m.WindowEndAt.Unix(),
		CreatedAt:       m.CreatedAt,
		ClosedAt:        m.ClosedAt,
		Tier:            cvt.ChangeTiers(m.Tier),
		RiskLevel:       relent.RiskLevel(m.RiskLevel),
		ChangeItemNames: cvt.ChangeItemNames(m.ChangeItemNames),
		Status:          relent.ChangeStatus(m.Status),
		StatusReason:    m.StatusReason,
		Type:            relent.ChangeType(m.Type),
	}
	if m.Result != nil {
		e.Result = pointer.Pointer(relent.ChangeResult(*m.Result))
	}

	if m.Detail != nil {
		e.Detail = cvt.ChangeDetail(m.Detail)
	}
	for _, d := range m.Deployments {
		e.Deployments = append(e.Deployments, cvt.ChangeDeployment(d))
	}
	if m.Notification != nil {
		e.Notification = cvt.ChangeNotification(m.Notification)
	}

	e.ReleaseID = m.ReleaseID.Uint64()
	e.ApplicantEmail = m.ApplicantEmail
	e.ApplicationName = m.ApplicationName
	if m.ApprovalID != nil {
		e.ApprovalID = pointer.Pointer(m.ApprovalID.Uint64())
	}
	e.PrevApprovalIDs = cvt.PrevApprovalIDs(m.PrevApprovalIDs)

	return e
}

func (cvt m2e) ChangeDetail(m *relmod.ChangeDetail) *relent.ChangeDetail {
	e := &relent.ChangeDetail{
		ID: m.ID.Uint64(),

		NeedOutage:        m.NeedOutage,
		Note:              m.Note,
		RollbackPlan:      m.RollbackPlan,
		StagingTestReport: m.StagingTestReport,
		Summary:           m.Summary,
	}

	return e
}

func (cvt m2e) ChangeNotification(m *relmod.ChangeNotification) *relent.ChangeNotification {
	e := &relent.ChangeNotification{
		ID: m.ID.Uint64(),

		LarkTopicCreatedAt: m.LarkTopicCreatedAt,
		LarkTopicID:        m.LarkTopicID,
		NotifiedToCloseAt:  m.NotifiedToCloseAt,
	}

	return e
}

func (cvt m2e) ChangeDeployment(m *relmod.ChangeDeployment) *relent.ChangeDeployment {
	info := relent.ChangeDeployInfo{}
	if err := json.Unmarshal(m.Info, &info); err != nil {
		panic(fmt.Errorf("failed unmarshal deployment info: %s", err.Error()))
	}

	e := &relent.ChangeDeployment{
		ID: m.ID.Uint64(),

		Order:      relent.ChangeDeployOrder(m.Order),
		Regions:    cvt.Regions(m.Regions),
		DeployedAt: m.DeployedAt,
		Status:     relent.ChangeDeployStatus(m.Status),
		Method:     relent.ChangeDeployMethod(m.Method),
		Info:       info,
		Note:       m.Note,
	}

	return e
}

func (cvt m2e) Regions(m string) []relent.RegionName {
	if m == "" {
		return []relent.RegionName{}
	}
	s := strings.Split(m, ",")
	return contutil.TransformSlice(s, contutil.FromString[relent.RegionName])
}

func (cvt m2e) ChangeItemNames(m string) []string {
	if m == "" {
		return []string{}
	}
	return strings.Split(m, ",")
}

func (cvt m2e) PrevApprovalIDs(m string) []uint64 {
	if m == "" {
		return []uint64{}
	}
	s := strings.Split(m, ",")
	return contutil.TransformSlice(s, func(item string) uint64 {
		id, err := strconv.ParseUint(item, 10, 64)
		if err != nil {
			panic(fmt.Errorf("failed parse prev approval id: %s", err.Error()))
		}
		return id
	})
}

func (cvt m2e) ChangeTiers(m string) []relent.ChangeTier {
	if m == "" {
		return []relent.ChangeTier{}
	}
	s := strings.Split(m, ",")
	return contutil.TransformSlice(s, contutil.FromString[relent.ChangeTier])
}

func (cvt m2e) Release(m *relmod.Release) *relent.ReleaseInRepo {
	e := &relent.ReleaseInRepo{
		ID:            m.ID.Uint64(),
		Name:          m.Name,
		Type:          relent.ReleaseType(m.Type),
		WindowStartAt: m.WindowStartAt,
		WindowEndAt:   m.WindowEndAt,
		ClosedAt:      m.ClosedAt,
		Status:        relent.ReleaseStatus(m.Status),
	}

	return e
}

func (cvt m2e) ReleaseTicket(m *relmod.ReleaseTicket) *relent.ReleaseTicketInRepo {
	e := &relent.ReleaseTicketInRepo{
		Name:          m.Name,
		Summary:       m.Summary,
		Status:        m.Status,
		Type:          relent.ReleaseTicketType(m.Type),
		AssigneeEmail: m.AssigneeEmail,
		ReleaseID:     m.ReleaseID.Uint64(),
		ReleaseName:   m.ReleaseName,
	}
	for _, prLoc := range m.GithubPRLocations {
		e.GithubPRLocations = append(e.GithubPRLocations, gitent.PRLocation(prLoc.PRLocation))
	}

	return e
}
