/*
 * SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
 * SPDX-License-Identifier: AGPL-3.0-only
 */

package locale

import (
	"context"

	"github.com/leonelquinteros/gotext"
	"golang.org/x/text/language"

	"dev.tandem.ws/tandem/camper/pkg/database"
)

type Locale struct {
	*gotext.Locale
	CurrencyPattern string
	Language        language.Tag
	Endonym         string
}

type Locales map[language.Tag]*Locale

func (m Locales) Tags() []language.Tag {
	keys := make([]language.Tag, len(m))
	i := 0
	for k := range m {
		keys[i] = k
		i++
	}
	return keys
}

func (m Locales) Get(lang string) (loc *Locale, ok bool) {
	tag, err := language.Parse(lang)
	if err != nil {
		return
	}
	loc, ok = m[tag]
	return
}

func GetAll(ctx context.Context, db *database.DB) (Locales, error) {
	availableLanguages, err := getAvailableLanguages(ctx, db)
	if err != nil {
		return nil, err
	}
	locales := Locales{}
	for _, lang := range availableLanguages {
		locale := lang.locale()
		locale.AddDomain("camper")
		locales[lang.tag] = locale
	}
	return locales, nil
}

func (l *Locale) Gettext(str string) string {
	return l.GetD(l.GetDomain(), str)
}

func (l *Locale) Pgettext(str string, ctx string) string {
	return l.GetDC(l.GetDomain(), str, ctx)
}

func (l *Locale) GettextNoop(str string) string {
	return str
}

func PgettextNoop(str string, ctx string) string {
	return str
}

func Match(acceptLanguage string, locales Locales, matcher language.Matcher) *Locale {
	t, _, err := language.ParseAcceptLanguage(acceptLanguage)
	if err != nil {
		return nil
	}
	var locale *Locale
	tag, _, confidence := matcher.Match(t...)
	if confidence > language.No {
		var ok bool
		locale, ok = locales[tag]
		for !ok && !tag.IsRoot() {
			tag = tag.Parent()
			locale, ok = locales[tag]
		}
	}
	return locale
}

type availableLanguage struct {
	tag             language.Tag
	endonym         string
	currencyPattern string
}

func getAvailableLanguages(ctx context.Context, db *database.DB) ([]*availableLanguage, error) {
	rows, err := db.Query(ctx, "select lang_tag, endonym, currency_pattern from language where selectable")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var languages []*availableLanguage
	for rows.Next() {
		lang := &availableLanguage{}
		var langTag string
		err = rows.Scan(&langTag, &lang.endonym, &lang.currencyPattern)
		if err != nil {
			return nil, err
		}
		lang.tag = language.MustParse(langTag)
		languages = append(languages, lang)
	}
	if rows.Err() != nil {
		return nil, rows.Err()
	}

	return languages, nil
}

func (lang *availableLanguage) locale() *Locale {
	return &Locale{
		gotext.NewLocale("locale", lang.tag.String()),
		lang.currencyPattern,
		lang.tag,
		lang.endonym,
	}
}