package grm

import (
	"errors"
	"fmt"
	"gitlab.com/revalus/grm/internal/commands"
	"gitlab.com/revalus/grm/internal/config"
	"gitlab.com/revalus/grm/internal/echo"
	"io"
	"sync"
)

const (
	AppName         = "Git repository manager"
	AppDescription  = "Manage your repository with simple grm"
	VERSION         = "0.3.2"
	errNotFoundTags = "no repository was found with the specified tags"
	errNotFoundName = "no repository was found with the specified name"
)

type GitRepositoryManager struct {
	cliArguments  config.CliArguments
	configuration config.Configuration
}

func (g *GitRepositoryManager) Parse(args []string) error {
	arguments, err := config.ParseCliArguments(AppName, AppDescription, args)
	if err != nil {
		fmt.Printf("Error: %v", err.Error())
		return err
	}

	configFileContent, err := getFileContent(arguments.ConfigurationFile)
	if err != nil {
		fmt.Printf("Error: %v", err.Error())
		return err
	}

	fileExtension, err := getFileExtension(arguments.ConfigurationFile)
	if err != nil {
		fmt.Printf("Error: %v", err.Error())
		return err
	}

	configuration, err := config.GetRepositoryConfig(configFileContent, fileExtension)
	if err != nil {
		fmt.Printf("Error: %v", err.Error())
		return err
	}

	g.cliArguments = arguments
	g.configuration = configuration
	return nil
}

func (g *GitRepositoryManager) Run(w io.Writer) int {

	echo.Color(g.cliArguments.Color)
	echo.Output(w)

	exitCode := 0

	if len(g.cliArguments.LimitToTags) != 0 {
		err := g.limitRepositoriesToTags()
		if err != nil {
			echo.ErrorfMsg(err.Error())
			return 1
		}
	}

	if g.cliArguments.LimitToName != "" {
		err := g.limitRepositoryToName()
		if err != nil {
			echo.ErrorfMsg(err.Error())
			return 1
		}
	}

	if g.cliArguments.Sync && exitCode == 0 {
		echo.InfoFMsg("Synchronizing repositories")
		sync := commands.NewSynchronizer(g.configuration.Workspace)
		g.runCommand(sync)
		echo.InfoFMsg("All repositories are synced")
	}

	if g.cliArguments.Status && exitCode == 0 {
		echo.InfoFMsg("Current status of repositories")
		status := commands.NewStatusChecker(g.configuration.Workspace)
		g.runCommand(status)
	}

	if g.cliArguments.Version {
		echo.InfoFMsg("Current version: %v", VERSION)
	}
	return exitCode
}

func describeStatus(status commands.CommandStatus) {
	if status.Error {
		echo.RedMessageF("Repository \"%v\": an error occurred: %v", status.Name, status.Message)
		return
	}

	if status.Changed {
		echo.YellowMessageF("Repository \"%v\": %v", status.Name, status.Message)
	} else {
		echo.GreenMessageF("Repository \"%v\": %v", status.Name, status.Message)
	}
}

func (g *GitRepositoryManager) limitRepositoriesToTags() error {
	limitedTagsTmp := []config.RepositoryConfig{}

	for _, item := range g.configuration.Repositories {
		if checkAnyOfItemInSlice(item.Tags, g.cliArguments.LimitToTags) {
			limitedTagsTmp = append(limitedTagsTmp, item)
		}
	}
	if len(limitedTagsTmp) == 0 {
		return errors.New(errNotFoundTags)
	}
	g.configuration.Repositories = reverseRepositoryConfigs(limitedTagsTmp)
	return nil
}

func (g *GitRepositoryManager) limitRepositoryToName() error {
	for _, item := range g.configuration.Repositories {
		if g.cliArguments.LimitToName == item.Name {
			g.configuration.Repositories = []config.RepositoryConfig{item}
			return nil
		}
	}
	return errors.New(errNotFoundName)
}

func (g *GitRepositoryManager) runCommand(cmd commands.Command) {
	routines := make(chan struct{}, g.cliArguments.Routines)

	var wg sync.WaitGroup
	for _, repo := range g.configuration.Repositories {

		if repo.Skip && !g.cliArguments.IgnoreSkipped {
			continue
		}

		wg.Add(1)

		go func(r config.RepositoryConfig) {
			defer wg.Done()
			routines <- struct{}{}
			describeStatus(cmd.Command(r))

			<-routines
		}(repo)
	}
	wg.Wait()
}