package common

/*
 * This file is part of Autogits.
 *
 * Copyright © 2024 SUSE LLC
 *
 * Autogits is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 2 of the License, or (at your option) any later
 * version.
 *
 * Autogits is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * Foobar. If not, see <https://www.gnu.org/licenses/>.
 */

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
)

//go:generate mockgen -source=config.go -destination=mock/config.go -typed

type ConfigFile struct {
	GitProjectName []string
}

type ReviewGroup struct {
	Name      string
	Reviewers []string
}

type AutogitConfig struct {
	Workflows      []string // [pr, direct, test]
	Organization   string
	GitProjectName string   // Organization/GitProjectName.git is PrjGit
	Branch         string   // branch name of PkgGit that aligns with PrjGit submodules
	Reviewers      []string // only used by `pr` workflow
	ReviewGroups   []ReviewGroup
}

type AutogitConfigs []*AutogitConfig

func ReadConfig(reader io.Reader) (*ConfigFile, error) {
	data, err := io.ReadAll(reader)
	if err != nil {
		return nil, fmt.Errorf("Error reading config data: %w", err)
	}

	config := ConfigFile{}
	if err := json.Unmarshal(data, &config.GitProjectName); err != nil {
		return nil, fmt.Errorf("Error parsing Git Project paths: %w", err)
	}

	return &config, nil
}

func ReadConfigFile(filename string) (*ConfigFile, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, fmt.Errorf("Cannot open config file for reading. err: %w", err)
	}
	defer file.Close()

	return ReadConfig(file)
}

type GiteaFileContentAndRepoFetcher interface {
	GiteaFileContentReader
	GiteaRepoFetcher
}

func ReadWorkflowConfig(gitea GiteaFileContentAndRepoFetcher, git_project string) (*AutogitConfig, error) {
	hash := strings.Split(git_project, "#")
	branch := ""
	if len(hash) > 1 {
		branch = hash[1]
	}

	a := strings.Split(hash[0], "/")
	prjGitRepo := DefaultGitPrj
	switch len(a) {
	case 1:
	case 2:
		prjGitRepo = a[1]
	default:
		return nil, fmt.Errorf("Missing org/repo in projectgit: %s", git_project)
	}

	data, _, err := gitea.GetRepositoryFileContent(a[0], prjGitRepo, branch, "workflow.config")
	if err != nil {
		return nil, fmt.Errorf("Error fetching 'workflow.config' for %s/%s#%s: %w", a[0], prjGitRepo, branch, err)
	}

	var config AutogitConfig
	if err := json.Unmarshal(data, &config); err != nil {
		return nil, fmt.Errorf("Error parsing workflow config file: %s: %w", string(data), err)
	}

	if len(config.Organization) < 1 {
		config.Organization = a[0]
	}
	config.GitProjectName = a[0] + "/" + prjGitRepo
	if len(branch) == 0 {
		if r, err := gitea.GetRepository(a[0], prjGitRepo); err == nil {
			branch = r.DefaultBranch
		} else {
			return nil, fmt.Errorf("Failed to read workflow config in %s: %w", git_project, err)
		}
	}
	config.GitProjectName = config.GitProjectName + "#" + branch
	return &config, nil
}

func ResolveWorkflowConfigs(gitea GiteaFileContentAndRepoFetcher, config *ConfigFile) (AutogitConfigs, error) {
	configs := make([]*AutogitConfig, 0, len(config.GitProjectName))
	for _, git_project := range config.GitProjectName {
		c, err := ReadWorkflowConfig(gitea, git_project)
		if err != nil {
			// can't sync, so ignore for now
			log.Println(err)
		} else {
			configs = append(configs, c)
		}
	}

	return configs, nil
}

func (configs AutogitConfigs) GetPrjGitConfig(org, repo, branch string) *AutogitConfig {
	prjgit := org + "/" + repo + "#" + branch
	for _, c := range configs {
		if c.GitProjectName == prjgit {
			return c
		}
		if c.Organization == org && c.Branch == branch {
			return c
		}
	}

	return nil
}

func (config *AutogitConfig) GetReviewGroupMembers(reviewer string) ([]string, error) {
	for _, g := range config.ReviewGroups {
		if g.Name == reviewer {
			return g.Reviewers, nil
		}
	}

	return nil, errors.New("User " + reviewer + " not found as group reviewer for " + config.GitProjectName)
}
