package apps

import (
	"errors"
	"fmt"
	"os"

	"github.com/dokku/dokku/plugins/common"
)

// CommandClone clones an app
func CommandClone(oldAppName string, newAppName string, skipDeploy bool, ignoreExisting bool) error {
	if oldAppName == "" {
		return errors.New("Please specify an app to run the command on")
	}

	if newAppName == "" {
		return errors.New("Please specify an new app name")
	}

	if err := common.VerifyAppName(oldAppName); err != nil {
		return err
	}

	if err := common.IsValidAppName(newAppName); err != nil {
		return err
	}

	if err := appExists(newAppName); err == nil {
		if ignoreExisting {
			common.LogWarn("Name is already taken")
			return nil
		}

		return errors.New("Name is already taken")
	}

	common.LogInfo1Quiet(fmt.Sprintf("Cloning %s to %s", oldAppName, newAppName))
	if err := createApp(newAppName); err != nil {
		return err
	}

	_, err := common.CallPlugnTrigger(common.PlugnTriggerInput{
		Trigger:     "post-app-clone-setup",
		Args:        []string{oldAppName, newAppName},
		StreamStdio: true,
	})
	if err != nil {
		return err
	}

	if skipDeploy {
		os.Setenv("SKIP_REBUILD", "true")
	}

	_, err = common.CallPlugnTrigger(common.PlugnTriggerInput{
		Trigger:     "git-has-code",
		Args:        []string{newAppName},
		StreamStdio: true,
	})
	if err != nil {
		os.Setenv("SKIP_REBUILD", "true")
	}

	_, err = common.CallPlugnTrigger(common.PlugnTriggerInput{
		Trigger:     "post-app-clone",
		Args:        []string{oldAppName, newAppName},
		StreamStdio: true,
	})
	return err
}

// CommandCreate creates app via command line
func CommandCreate(appName string) error {
	if err := common.IsValidAppName(appName); err != nil {
		return err
	}

	return createApp(appName)
}

// CommandDestroy destroys an app
func CommandDestroy(appName string, force bool) error {
	if err := common.VerifyAppName(appName); err != nil {
		return err
	}

	if force {
		os.Setenv("DOKKU_APPS_FORCE_DELETE", "1")
	}

	return destroyApp(appName)
}

// CommandExists checks if an app exists
func CommandExists(appName string) error {
	return appExists(appName)
}

// CommandList lists all apps
func CommandList() error {
	common.LogInfo2Quiet("My Apps")
	apps, err := common.DokkuApps()
	if err != nil {
		common.LogWarn(err.Error())
		return nil
	}

	for _, appName := range apps {
		common.Log(appName)
	}

	return nil
}

// CommandLock locks an app for deployment
func CommandLock(appName string) error {
	if err := common.VerifyAppName(appName); err != nil {
		return err
	}

	lockPath := getLockPath(appName)
	if _, err := os.Create(lockPath); err != nil {
		return errors.New("Unable to create deploy lock")
	}

	common.LogInfo1("Deploy lock created")
	return nil
}

// CommandLocked checks if an app is locked for deployment
func CommandLocked(appName string) error {
	if err := common.VerifyAppName(appName); err != nil {
		return err
	}

	if appIsLocked(appName) {
		common.LogQuiet("Deploy lock exists")
		return nil

	}
	return errors.New("Deploy lock does not exist")
}

// CommandRename renames an app
func CommandRename(oldAppName string, newAppName string, skipDeploy bool) error {
	if oldAppName == "" {
		return errors.New("Please specify an app to run the command on")
	}

	if newAppName == "" {
		return errors.New("Please specify an new app name")
	}

	if err := common.VerifyAppName(oldAppName); err != nil {
		return err
	}

	if err := common.IsValidAppName(newAppName); err != nil {
		return err
	}

	if err := appExists(newAppName); err == nil {
		return errors.New("Name is already taken")
	}

	common.LogInfo1Quiet(fmt.Sprintf("Renaming %s to %s", oldAppName, newAppName))
	if err := createApp(newAppName); err != nil {
		return err
	}

	_, err := common.CallPlugnTrigger(common.PlugnTriggerInput{
		Trigger:     "post-app-rename-setup",
		Args:        []string{oldAppName, newAppName},
		StreamStdio: true,
	})
	if err != nil {
		return err
	}

	os.Setenv("DOKKU_APPS_FORCE_DELETE", "1")
	if err := destroyApp(oldAppName); err != nil {
		return err
	}

	if skipDeploy {
		os.Setenv("SKIP_REBUILD", "true")
	}

	_, err = common.CallPlugnTrigger(common.PlugnTriggerInput{
		Trigger:     "git-has-code",
		Args:        []string{newAppName},
		StreamStdio: true,
	})
	if err != nil {
		os.Setenv("SKIP_REBUILD", "true")
	}

	_, err = common.CallPlugnTrigger(common.PlugnTriggerInput{
		Trigger:     "post-app-rename",
		Args:        []string{oldAppName, newAppName},
		StreamStdio: true,
	})
	return err
}

// CommandReport displays an app report for one or more apps
func CommandReport(appName string, format string, infoFlag string) error {
	if len(appName) == 0 {
		apps, err := common.DokkuApps()
		if err != nil {
			if errors.Is(err, common.NoAppsExist) {
				common.LogWarn(err.Error())
				return nil
			}
			return err
		}
		for _, appName := range apps {
			if err := ReportSingleApp(appName, format, infoFlag); err != nil {
				return err
			}
		}
		return nil
	}

	return ReportSingleApp(appName, format, infoFlag)
}

// CommandUnlock unlocks an app for deployment
func CommandUnlock(appName string) error {
	if err := common.VerifyAppName(appName); err != nil {
		return err
	}

	lockfilePath := getLockPath(appName)
	if _, err := os.Stat(lockfilePath); !os.IsNotExist(err) {
		common.LogWarn("A deploy may be in progress.")
		common.LogWarn("Removing the app lock will not stop in progress deploys.")
	}

	if err := os.Remove(lockfilePath); err != nil {
		return errors.New("Unable to remove deploy lock")
	}

	common.LogInfo1("Deploy lock removed")
	return nil
}
