package i18n

import (
	"embed"
	"encoding/json"
	"fmt"
	"io/fs"
	"strings"

	"dario.cat/mergo"
	"github.com/cloudfoundry/jibber_jabber"
	"github.com/go-errors/errors"
	"github.com/samber/lo"
	"github.com/sirupsen/logrus"
)

func NewTranslationSetFromConfig(log *logrus.Entry, configLanguage string) (*TranslationSet, error) {
	languageCodes, err := getSupportedLanguageCodes()
	if err != nil {
		return nil, err
	}

	if configLanguage == "auto" {
		language := detectLanguage(jibber_jabber.DetectIETF)
		for _, languageCode := range languageCodes {
			if strings.HasPrefix(language, languageCode) {
				return newTranslationSet(log, languageCode)
			}
		}

		// Detecting a language that we don't have a translation for is not an
		// error, we'll just use English.
		return EnglishTranslationSet(), nil
	}

	if configLanguage == "en" {
		return EnglishTranslationSet(), nil
	}

	for _, key := range languageCodes {
		if key == configLanguage {
			return newTranslationSet(log, configLanguage)
		}
	}

	// Configuring a language that we don't have a translation for *is* an
	// error, though.
	return nil, errors.New("Language not found: " + configLanguage)
}

func newTranslationSet(log *logrus.Entry, language string) (*TranslationSet, error) {
	log.Info("language: " + language)

	baseSet := EnglishTranslationSet()

	if language != "en" {
		translationSet, err := readLanguageFile(language)
		if err != nil {
			return nil, err
		}
		err = mergo.Merge(baseSet, *translationSet, mergo.WithOverride)
		if err != nil {
			return nil, err
		}
	}

	return baseSet, nil
}

//go:embed translations/*.json
var embedFS embed.FS

// getSupportedLanguageCodes gets all the supported language codes.
// Note: this doesn't include "en"
func getSupportedLanguageCodes() ([]string, error) {
	dirEntries, err := embedFS.ReadDir("translations")
	if err != nil {
		return nil, err
	}
	return lo.Map(dirEntries, func(entry fs.DirEntry, _ int) string {
		return strings.TrimSuffix(entry.Name(), ".json")
	}), nil
}

func readLanguageFile(languageCode string) (*TranslationSet, error) {
	jsonData, err := embedFS.ReadFile(fmt.Sprintf("translations/%s.json", languageCode))
	if err != nil {
		return nil, err
	}
	var translationSet TranslationSet
	err = json.Unmarshal(jsonData, &translationSet)
	if err != nil {
		return nil, err
	}
	return &translationSet, nil
}

// GetTranslationSets gets all the translation sets, keyed by language code
// This includes "en".
func GetTranslationSets() (map[string]*TranslationSet, error) {
	languageCodes, err := getSupportedLanguageCodes()
	if err != nil {
		return nil, err
	}

	result := make(map[string]*TranslationSet)
	result["en"] = EnglishTranslationSet()

	for _, languageCode := range languageCodes {
		translationSet, err := readLanguageFile(languageCode)
		if err != nil {
			return nil, err
		}
		result[languageCode] = translationSet
	}

	return result, nil
}

// detectLanguage extracts user language from environment
func detectLanguage(langDetector func() (string, error)) string {
	if userLang, err := langDetector(); err == nil {
		return userLang
	}

	return "C"
}
