package main

import (
	"os"
	"sort"
	"time"

	"github.com/google/uuid"
	"gopkg.in/yaml.v2"
)

type (
	yamlDB struct {
		addr      string
		Knowledge knowledge
		Users     map[IDU]user
	}
	knowledge struct {
		Questions map[IDQ]Question
		Answers   map[IDA]Answer
	}
	user struct {
		Tags       tags
		Cadence    duration
		Resolution int
	}
	tags struct {
		Assignments []IDT
	}
)

func newYamlDB(p string) (yamlDB, error) {
	db := yamlDB{
		addr: p,
		Knowledge: knowledge{
			Questions: map[IDQ]Question{},
			Answers:   map[IDA]Answer{},
		},
		Users: map[IDU]user{},
	}
	if b, err := os.ReadFile(p); err != nil {
		return yamlDB{}, err
	} else if err := yaml.Unmarshal(b, &db); err != nil {
		return yamlDB{}, err
	}
	return db, nil
}

func (db yamlDB) Close() {
	if os.Getenv("RO") != "" {
		return
	}
	b, err := yaml.Marshal(db)
	if err != nil {
		panic(err)
	}
	if err := os.Rename(db.addr, db.addr+".bak"); err != nil {
		panic(err)
	}
	if err := os.WriteFile(db.addr, b, os.ModePerm); err != nil {
		panic(err)
	}
}

func (db yamlDB) HistoryOf(user IDU) map[IDQ][]Answer {
	result := map[IDQ][]Answer{}
	for _, v := range db.Knowledge.Answers {
		if v.Author == user {
			result[v.Q] = append(result[v.Q], v)
		}
	}
	for _, tag := range db.Users[user].Tags.Assignments {
		for qid, q := range db.Knowledge.Questions {
			if _, ok := result[qid]; !ok && q.Tagged(tag) {
				result[qid] = []Answer{}
			}
		}
	}
	for _, v := range result {
		sort.Slice(v, func(i, j int) bool {
			return v[i].TS < v[j].TS
		})
	}
	return result
}

func (db yamlDB) Next(user IDU, q IDQ) time.Time {
	allHistory := db.HistoryOf(user)
	history := allHistory[q]
	last := db.lastTS(user, q)
	log := make([]int, 0, len(history))
	for i := range history {
		v := 0
		if history[i].Pass {
			v = 5
		}
		log = append(log, v)
	}
	return SM2Next(last, log, db.cadence(user))
}

func (db yamlDB) UserResolution(user IDU) int {
	u := db.Users[user]
	if u.Resolution == 0 {
		return 40
	}
	return u.Resolution
}

func (db yamlDB) cadence(user IDU) time.Duration {
	u := db.Users[user]
	if u.Cadence == 0 {
		return time.Hour * 24
	}
	return time.Duration(u.Cadence)
}

func (db yamlDB) Question(q IDQ) Question {
	return db.Knowledge.Questions[q]
}

func (db yamlDB) LastAnswer(user IDU, q IDQ) (IDA, Answer) {
	var maxk IDA
	var maxv Answer
	for k, v := range db.Knowledge.Answers {
		if v.Q == q && v.Author == user {
			if maxv.TS < v.TS {
				maxk = k
				maxv = v
			}
		}
	}
	return maxk, maxv
}

func (db yamlDB) Answer(a IDA) Answer {
	return db.Knowledge.Answers[a]
}

func (db yamlDB) PushAnswer(user IDU, q IDQ, a Renderable, pass bool) error {
	uuid := IDA(uuid.New().String())
	db.Knowledge.Answers[uuid] = Answer{
		Q:      q,
		A:      a,
		TS:     time.Now().UnixNano(),
		Author: user,
		Pass:   pass,
	}
	return nil
}

func (db yamlDB) lastTS(user IDU, q IDQ) time.Time {
	allHistory := db.HistoryOf(user)
	history := allHistory[q]
	if len(history) == 0 {
		return time.Unix(0, 0)
	}
	return time.Unix(0, history[len(history)-1].TS)
}
