package configFile

import (
	"fmt"
	"io/ioutil"

	"gopkg.in/yaml.v2"
)

// The above code defines a configuration struct for software projects with sections for software,
// environment variables, directories, and pipeline configurations.
// @property Sections - A map of SectionConfig objects, where each key is a string representing the
// name of the section.
type Config struct {
	Sections map[string]SectionConfig `yaml:"category"`
}

type SectionConfig struct {
	Softwares             map[string]SoftwareConfig  `yaml:"software"`
	EnvironmentsVariables map[string]string          `yaml:"environments variables"`
	Directory             map[string]DirectoryConfig `yaml:"directory"`
	Pipelines             map[string]PipelineConfig  `yaml:"pipelines"`
}

type SoftwareConfig struct {
	Name        string `yaml:"name"`
	Version     string `yaml:"version"`
	Author      string `yaml:"author"`
	Location    string `yaml:"location"`
	Description string `yaml:"description"`
}

type DirectoryConfig struct {
	Source struct {
		Path []string `yaml:"path"`
	} `yaml:"source"`
	Test struct {
		Path []string `yaml:"path"`
	} `yaml:"test"`
	Release struct {
		Path []string `yaml:"path"`
	} `yaml:"release"`
}

type PipelineConfig struct {
	Build string `yaml:"build"`
	Test  string `yaml:"test"`
	Lint  string `yaml:"lint"`
	// Compile string `yaml:"compile"`
	// Package string `yaml:"package"`
}

// The function loads a configuration file in YAML format and returns a pointer to a Config struct or
// an error.
func LoadConfig(filename string) (*Config, error) {
	var config Config

	source, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	// print source
	// fmt.Println(string(source))

	err = yaml.Unmarshal(source, &config)
	if err != nil {
		return nil, err
	}

	return &config, nil
}

// function GetListCategory return list category from config file
func (c *Config) GetListCategory() ([]string, error) {
	var categories []string
	for categoryName := range c.Sections {
		categories = append(categories, categoryName)
	}

	return categories, nil
}

// This is a method defined on the `Config` struct that returns a list of software names for a given
// section. It takes in a `sectionName` string as a parameter and returns a slice of strings and an
// error.
func (c *Config) GetListSoftware(sectionName string) ([]string, error) {
	section, ok := c.Sections[sectionName]
	if !ok {
		return nil, fmt.Errorf("section '%s' not found", sectionName)
	}

	var softwares []string
	for softwareName := range section.Softwares {
		softwares = append(softwares, softwareName)
	}

	return softwares, nil
}

// This is a method defined on the `Config` struct that takes in two string parameters `sectionName`
// and `SoftwareName`. It returns a pointer to a `SoftwareConfig` struct and an error.
func (c *Config) GetSoftware(sectionName, SoftwareName string) (*SoftwareConfig, error) {
	section, ok := c.Sections[sectionName]
	if !ok {
		return nil, fmt.Errorf("section '%s' not found", sectionName)
	}

	software, ok := section.Softwares[SoftwareName]
	if !ok {
		return nil, fmt.Errorf("logiciel '%s' not found in section '%s'", SoftwareName, sectionName)
	}

	return &software, nil
}

// get list environment variables
func (c *Config) GetListEnvironmentVariables(sectionName string) ([]string, error) {
	section, ok := c.Sections[sectionName]
	if !ok {
		return nil, fmt.Errorf("section '%s' not found", sectionName)
	}

	var environmentVariables []string
	for environmentVariableName := range section.EnvironmentsVariables {
		environmentVariables = append(environmentVariables, environmentVariableName)
	}

	return environmentVariables, nil
}

// This is a method defined on the `Config` struct that takes in two string parameters `sectionName`
// and `variableName`. It returns a string value and an error. The method looks for the specified
// section in the configuration and then looks for the specified environment variable within that
// section. If the section or variable is not found, an error is returned. If the variable is found,
// its value is returned.
func (c *Config) GetEnvironmentVariables(sectionName, variableName string) (string, error) {
	section, ok := c.Sections[sectionName]
	if !ok {
		return "", fmt.Errorf("section '%s' not found", sectionName)
	}

	variable, ok := section.EnvironmentsVariables[variableName]
	if !ok {
		return "", fmt.Errorf("variable '%s' not found in section '%s'", variableName, sectionName)
	}

	return variable, nil
}

// This is a method defined on the `Config` struct that takes in two string parameters `sectionName`
// and `directoryName`. It returns a pointer to a `DirectoryConfig` struct and an error. The method
// looks for the specified section in the configuration and then looks for the specified directory
// within that section. If the section or directory is not found, an error is returned. If the
// directory is found, its configuration is returned.
func (c *Config) GetDirectory(sectionName, directoryName string) (*DirectoryConfig, error) {
	section, ok := c.Sections[sectionName]
	if !ok {
		return nil, fmt.Errorf("section '%s' not found", sectionName)
	}

	directory, ok := section.Directory[directoryName]

	if !ok {
		return nil, fmt.Errorf("directory '%s' not found in section '%s'", directoryName, sectionName)
	}

	return &directory, nil
}

// This is a method defined on the `Config` struct that takes in two string parameters `sectionName`
// and `pipelineName`. It returns a pointer to a `PipelineConfig` struct and an error. The method looks
// for the specified section in the configuration and then looks for the specified pipeline within that
// section. If the section or pipeline is not found, an error is returned. If the pipeline is found,
// its configuration is returned.
func (c *Config) GetPipeline(sectionName, pipelineName string) (*PipelineConfig, error) {
	section, ok := c.Sections[sectionName]
	if !ok {
		return nil, fmt.Errorf("section '%s' not found", sectionName)
	}

	pipeline, ok := section.Pipelines[pipelineName]

	if !ok {
		return nil, fmt.Errorf("pipeline '%s' not found in section '%s'", pipelineName, sectionName)
	}

	return &pipeline, nil
}
