package app

import (
	"bytes"
	"context"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strings"
	"sync"

	"github.com/deluan/navidrome/consts"
	"github.com/deluan/navidrome/log"
	"github.com/deluan/navidrome/resources"
	"github.com/deluan/rest"
)

type translation struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	Data string `json:"data"`
}

var (
	once         sync.Once
	translations map[string]translation
)

func newTranslationRepository(context.Context) rest.Repository {
	dir := resources.AssetFile()
	if err := loadTranslations(dir); err != nil {
		log.Error("Error loading translation files", err)
	}
	return &translationRepository{}
}

type translationRepository struct{}

func (r *translationRepository) Read(id string) (interface{}, error) {
	if t, ok := translations[id]; ok {
		return t, nil
	}
	return nil, rest.ErrNotFound
}

// Simple Count implementation. Does not support any `options`
func (r *translationRepository) Count(options ...rest.QueryOptions) (int64, error) {
	return int64(len(translations)), nil
}

// Simple ReadAll implementation, only returns IDs. Does not support any `options`
func (r *translationRepository) ReadAll(options ...rest.QueryOptions) (interface{}, error) {
	var result []translation
	for _, t := range translations {
		t.Data = ""
		result = append(result, t)
	}
	return result, nil
}

func (r *translationRepository) EntityName() string {
	return "translation"
}

func (r *translationRepository) NewInstance() interface{} {
	return &translation{}
}

func loadTranslations(fs http.FileSystem) (loadError error) {
	once.Do(func() {
		translations = make(map[string]translation)
		dir, err := fs.Open(consts.I18nFolder)
		if err != nil {
			loadError = err
			return
		}
		files, err := dir.Readdir(0)
		if err != nil {
			loadError = err
			return
		}
		var languages []string
		for _, f := range files {
			t, err := loadTranslation(fs, f.Name())
			if err != nil {
				log.Error("Error loading translation file", "file", f.Name(), err)
				continue
			}
			translations[t.ID] = t
			languages = append(languages, t.ID)
		}
		log.Info("Loading translations", "languages", languages)
	})
	return
}

func loadTranslation(fs http.FileSystem, fileName string) (translation translation, err error) {
	// Get id and full path
	name := filepath.Base(fileName)
	id := strings.TrimSuffix(name, filepath.Ext(name))
	filePath := filepath.Join(consts.I18nFolder, name)

	// Load translation from json file
	file, err := fs.Open(filePath)
	if err != nil {
		return
	}
	data, err := ioutil.ReadAll(file)
	if err != nil {
		return
	}
	var out map[string]interface{}
	if err = json.Unmarshal(data, &out); err != nil {
		return
	}

	// Compress JSON
	buf := new(bytes.Buffer)
	if err = json.Compact(buf, data); err != nil {
		return
	}

	translation.Data = buf.String()
	translation.Name = out["languageName"].(string)
	translation.ID = id
	return
}

var _ rest.Repository = (*translationRepository)(nil)
