package migrator

import (
	"strconv"
	"strings"

	"github.com/boltdb/bolt"
	"github.com/portainer/portainer/api"
	"github.com/portainer/portainer/api/bolt/internal"
	"github.com/portainer/portainer/api/bolt/stack"
)

func (m *Migrator) updateEndpointsToVersion12() error {
	legacyEndpoints, err := m.endpointService.Endpoints()
	if err != nil {
		return err
	}

	for _, endpoint := range legacyEndpoints {
		endpoint.Tags = []string{}

		err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
		if err != nil {
			return err
		}
	}

	return nil
}

func (m *Migrator) updateEndpointGroupsToVersion12() error {
	legacyEndpointGroups, err := m.endpointGroupService.EndpointGroups()
	if err != nil {
		return err
	}

	for _, group := range legacyEndpointGroups {
		group.Tags = []string{}

		err = m.endpointGroupService.UpdateEndpointGroup(group.ID, &group)
		if err != nil {
			return err
		}
	}

	return nil
}

type legacyStack struct {
	ID          string               `json:"Id"`
	Name        string               `json:"Name"`
	EndpointID  portainer.EndpointID `json:"EndpointId"`
	SwarmID     string               `json:"SwarmId"`
	EntryPoint  string               `json:"EntryPoint"`
	Env         []portainer.Pair     `json:"Env"`
	ProjectPath string
}

func (m *Migrator) updateStacksToVersion12() error {
	legacyStacks, err := m.retrieveLegacyStacks()
	if err != nil {
		return err
	}

	for _, legacyStack := range legacyStacks {
		err := m.convertLegacyStack(&legacyStack)
		if err != nil {
			return err
		}
	}

	return nil
}

func (m *Migrator) convertLegacyStack(s *legacyStack) error {
	stackID := m.stackService.GetNextIdentifier()
	stack := &portainer.Stack{
		ID:         portainer.StackID(stackID),
		Name:       s.Name,
		Type:       portainer.DockerSwarmStack,
		SwarmID:    s.SwarmID,
		EndpointID: 0,
		EntryPoint: s.EntryPoint,
		Env:        s.Env,
	}

	stack.ProjectPath = strings.Replace(s.ProjectPath, s.ID, strconv.Itoa(stackID), 1)
	err := m.fileService.Rename(s.ProjectPath, stack.ProjectPath)
	if err != nil {
		return err
	}

	err = m.deleteLegacyStack(s.ID)
	if err != nil {
		return err
	}

	return m.stackService.CreateStack(stack)
}

func (m *Migrator) deleteLegacyStack(legacyID string) error {
	return m.db.Update(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(stack.BucketName))
		return bucket.Delete([]byte(legacyID))
	})
}

func (m *Migrator) retrieveLegacyStacks() ([]legacyStack, error) {
	var legacyStacks = make([]legacyStack, 0)
	err := m.db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(stack.BucketName))
		cursor := bucket.Cursor()

		for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
			var stack legacyStack
			err := internal.UnmarshalObject(v, &stack)
			if err != nil {
				return err
			}
			legacyStacks = append(legacyStacks, stack)
		}

		return nil
	})

	return legacyStacks, err
}