package hubops

import (
	"context"
	"fmt"
	"os"

	log "github.com/sirupsen/logrus"

	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
)

// RemoveInstallLink removes the item's symlink between the installation directory and the local hub.
func RemoveInstallLink(i *cwhub.Item) error {
	stat, err := os.Lstat(i.State.LocalPath)
	if err != nil {
		return err
	}

	// if it's managed by hub, it's a symlink to csconfig.GConfig.hub.HubDir / ...
	if stat.Mode()&os.ModeSymlink == 0 {
		return fmt.Errorf("%s isn't managed by hub", i.Name)
	}

	target, err := os.Readlink(i.State.LocalPath)
	if err != nil {
		return fmt.Errorf("while reading symlink: %w", err)
	}

	if target != i.State.DownloadPath {
		return fmt.Errorf("%s isn't managed by hub", i.Name)
	}

	if err := os.Remove(i.State.LocalPath); err != nil {
		return fmt.Errorf("while removing symlink: %w", err)
	}

	i.State.LocalPath = ""

	return nil
}

// DisableCommand uninstalls an item and its dependencies, ensuring that no
// sub-item is left in an inconsistent state.
type DisableCommand struct {
	Item  *cwhub.Item
	Force bool
}

func NewDisableCommand(item *cwhub.Item, force bool) *DisableCommand {
	return &DisableCommand{Item: item, Force: force}
}

func (c *DisableCommand) Prepare(plan *ActionPlan) (bool, error) {
	i := c.Item

	if i.State.IsLocal() {
		log.Warnf("%s is a local item, please delete manually", i.FQName())
		return false, nil
	}

	if i.State.Tainted && !c.Force {
		return false, fmt.Errorf("%s is tainted, use '--force' to remove", i.Name)
	}

	if !i.State.IsInstalled() {
		return false, nil
	}

	subsToRemove, err := i.SafeToRemoveDeps()
	if err != nil {
		return false, err
	}

	for _, sub := range subsToRemove {
		if !sub.State.IsInstalled() {
			continue
		}

		if err := plan.AddCommand(NewDisableCommand(sub, c.Force)); err != nil {
			return false, err
		}
	}

	return true, nil
}

func (c *DisableCommand) Run(_ context.Context, plan *ActionPlan) error {
	i := c.Item

	fmt.Fprintln(os.Stdout, "disabling " + colorizeItemName(i.FQName()))

	if err := RemoveInstallLink(i); err != nil {
		return fmt.Errorf("while disabling %s: %w", i.FQName(), err)
	}

	plan.ReloadNeeded = true

	i.State.Tainted = false

	return nil
}

func (*DisableCommand) OperationType() string {
	return "disable"
}

func (c *DisableCommand) ItemType() string {
	return c.Item.Type
}

func (c *DisableCommand) Detail() string {
	return colorizeItemName(c.Item.Name)
}
