package i18n

import (
	"notabug.org/apiote/amuse/utils"

	"github.com/BurntSushi/toml"
	"github.com/bytesparadise/libasciidoc"

	"bytes"
	"context"
	"fmt"
	"golang.org/x/text/language"
	"html/template"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strings"
	"time"
)

type Translation struct {
	Global map[string]string
	Index  map[string]string
	Search map[string]string
	Film   map[string]string
	Serie  map[string]string
	Person map[string]string
	About  map[string]string
	Error  map[string]string
}

var serverLangs []language.Tag

func LoadServerLangs() {
	var tags []string
	var defaultLocale string
	defaultLocaleIndex := -1
	filepath.Walk("i18n", func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			if filepath.Ext(path) == ".toml" {
				tag := strings.Replace(filepath.Base(path), ".toml", "", 1)
				if tag == "default" {
					p, _ := os.Readlink(path)
					defaultLocale = strings.Replace(filepath.Base(p), ".toml", "", 1)
				} else {
					tags = append(tags, tag)
				}
			}
		}
		return nil
	})

	for i, tag := range tags {
		if tag == defaultLocale {
			defaultLocaleIndex = i
		}
	}
	tags[0], tags[defaultLocaleIndex] = tags[defaultLocaleIndex], tags[0]

	for _, tag := range tags {
		serverLangs = append(serverLangs, language.Make(tag))
	}
}

func Match(acceptLanguages []language.Tag) string {
	var matcher = language.NewMatcher(serverLangs)
	tag, _, _ := matcher.Match(acceptLanguages...)
	return tag.String()
}

func LoadStrings(language string) (Translation, error) {
	var strings Translation
	var defaultTranslation Translation
	_, err := toml.DecodeFile(filepath.Join("i18n", language+".toml"), &strings)
	_, err2 := toml.DecodeFile(filepath.Join("i18n", "default.toml"), &defaultTranslation)
	err = utils.Or(err, err2)

	stringsValue := reflect.ValueOf(&strings).Elem()
	stringsType := stringsValue.Type()
	for i := 0; i < stringsValue.NumField(); i++ {
		stringsField := stringsValue.Field(i)
		if stringsField.IsNil() {
			stringsField.Set(reflect.MakeMap(reflect.TypeOf(map[string]string{})))
		}
		defaultValue := reflect.ValueOf(&defaultTranslation).Elem()
		for key, entry := range defaultValue.FieldByName(stringsType.Field(i).Name).Interface().(map[string]string) {
			if stringsField.Interface().(map[string]string)[key] == "" {
				stringsField.Interface().(map[string]string)[key] = entry
			}
		}
	}

	return strings, err
}

func FormatDate(date time.Time, format string, translation map[string]string) string {
	// todo %a
	format = strings.ReplaceAll(format, "%A", translation[date.Weekday().String()])
	// todo %b
	format = strings.ReplaceAll(format, "%B", translation[date.Month().String()])
	// %c intentionally ommitted
	format = strings.ReplaceAll(format, "%C", fmt.Sprintf("%d", date.Year()/100))
	format = strings.ReplaceAll(format, "%d", fmt.Sprintf("%02d", date.Day()))
	format = strings.ReplaceAll(format, "%D", fmt.Sprintf("%02d/%02d/%02d", date.Month(), date.Day(), date.Year()%100))
	format = strings.ReplaceAll(format, "%e", fmt.Sprintf("%2d", date.Day()))
	// todo %h == %b
	format = strings.ReplaceAll(format, "%H", fmt.Sprintf("%02d", date.Hour()))
	hour := date.Hour() % 12
	if hour == 0 {
		hour = 12
	}
	format = strings.ReplaceAll(format, "%I", fmt.Sprintf("%02d", hour))
	format = strings.ReplaceAll(format, "%j", fmt.Sprintf("%03d", date.YearDay()))
	format = strings.ReplaceAll(format, "%m", fmt.Sprintf("%02d", date.Month()))
	format = strings.ReplaceAll(format, "%M", fmt.Sprintf("%02d", date.Minute()))
	format = strings.ReplaceAll(format, "%n", "\n")
	// todo %p
	// %r intentionally ommitted
	format = strings.ReplaceAll(format, "%S", fmt.Sprintf("%02d", date.Second()))
	format = strings.ReplaceAll(format, "%t", "\t")
	format = strings.ReplaceAll(format, "%T", fmt.Sprintf("%02d:%02d:%02d", date.Hour(), date.Minute(), date.Second()%100))
	weekday := date.Weekday()
	if weekday == 0 {
		weekday = 7
	}
	format = strings.ReplaceAll(format, "%u", fmt.Sprintf("%d", weekday))
	// todo %U
	// todo %V
	format = strings.ReplaceAll(format, "%w", fmt.Sprintf("%d", date.Weekday()))
	// todo %W
	// %x intentionally ommitted
	// %X intentionally ommitted
	format = strings.ReplaceAll(format, "%y", fmt.Sprintf("%d", date.Year()%100))
	format = strings.ReplaceAll(format, "%Y", fmt.Sprintf("%d", date.Year()))
	zone, _ := date.Zone()
	format = strings.ReplaceAll(format, "%Z", zone)
	format = strings.ReplaceAll(format, "%%", "%")
	return format
}

func RenderAsciiDoc(asciidoc string) template.HTML {
	r := strings.NewReader(asciidoc)
	w := bytes.NewBuffer([]byte{})
	ctx := context.Background()
	libasciidoc.ConvertToHTML(ctx, r, w)
	output := bytes.ReplaceAll(w.Bytes(), []byte("\n"), []byte(""))

	divRegex, err := regexp.Compile("<\\/?div[^>]*>")
	pRegex, err2 := regexp.Compile("<\\/?p>")
	err = utils.Or(err, err2)

	output = divRegex.ReplaceAll(output, []byte(""))
	output = pRegex.ReplaceAll(output, []byte(""))

	return template.HTML(output)
}

func GetErrorData(code int, translation Translation, part string) string {
	key := fmt.Sprintf("%d_%s", code, part)
	return translation.Error[key]
}
