package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

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

type (
	DB struct {
		Knowledge Knowledge
		Users     map[string]User
		Cadence   []Duration
	}
	Knowledge struct {
		Questions map[string]Question
		Answers   map[string]Answer
	}
	Question struct {
		Q    string
		Tags []string
	}
	Answer struct {
		Q      string
		A      string
		TS     int64
		Author string
	}
	User struct {
		History map[string][]History
	}
	History struct {
		A    string
		TS   int64
		Pass bool
	}
	Duration time.Duration
)

func main() {
	if err := Main(); err != nil {
		panic(err)
	}
}

func Main() error {
	var db DB
	if b, err := os.ReadFile(os.Getenv("DB")); err != nil {
		return err
	} else if err := yaml.Unmarshal(b, &db); err != nil {
		return err
	}
	user := os.Getenv("USER")
	for q, history := range db.Users[user].History {
		log.Printf("%s/%s/%+v", user, q, history)
		if time.Until(db.Next(user, q)) > 0 {
			continue
		}
		question := db.Question(q)
		fmt.Printf("> Q: %s\n", question.Q)
		fmt.Printf("> %+v\n\t", question.Tags)
		response := readline()
		if lastAnswer := db.Answer(db.LastAnswer(user, q).A); lastAnswer.A != "" {
			fmt.Printf("> Last time, you responded:\n\t%s\n", lastAnswer.A)
		}
		fmt.Printf("> Did you pass this time? [Yn] ")
		pass := readline() == "n"
		if err := db.PushAnswer(user, q, response, pass); err != nil {
			return err
		}
		fmt.Println()
	}
	if b, _ := yaml.Marshal(db); len(b) > 0 {
		log.Printf("%s\n", b)
	}
	return nil
}

func readline() string {
	reader := bufio.NewReader(os.Stdin)
	text, _ := reader.ReadString('\n')
	return text
}

func (db DB) Question(q string) Question {
	return db.Knowledge.Questions[q]
}

func (db DB) Answer(a string) Answer {
	return db.Knowledge.Answers[a]
}

func (db DB) LastAnswer(user, q string) Answer {
	for _, v := range db.Knowledge.Answers {
		if v.Q == q && v.Author == user {
			return v
		}
	}
	return Answer{}
}

func (db DB) PushAnswer(user, q, a string, pass bool) error {
	uuid := uuid.New().String()
	db.Knowledge.Answers[uuid] = Answer{
		Q:      q,
		A:      a,
		TS:     time.Now().UnixNano(),
		Author: user,
	}
	db.Users[user].History[q] = append(db.Users[user].History[q], History{
		A:    uuid,
		Pass: pass,
	})
	return nil
}

func (db DB) Next(user, q string) time.Time {
	history := db.Users[user].History[q]
	progress := 0
	for i := range history {
		if history[i].Pass {
			progress += 1
		} else {
			progress -= 1
		}
	}
	if progress < 0 {
		progress = 0
	} else if progress > len(db.Cadence) {
		return time.Now().Add(time.Hour * 24 * 365 * 10)
	}
	return db.LastTS(user, q).Add(time.Duration(db.Cadence[progress]))
}

func (db DB) LastTS(user, q string) time.Time {
	max := int64(0)
	for _, v := range db.Users[user].History[q] {
		if v.TS > max {
			max = v.TS
		}
	}
	return time.Unix(0, max)
}

func (d Duration) MarshalYAML() (interface{}, error) {
	return time.Duration(d).String(), nil
}

func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	if err := unmarshal(&s); err != nil {
		return err
	}
	count := ""
	var ttl time.Duration
	for i := range s {
		if s[i] < '0' || s[i] > '9' {
			n, err := strconv.Atoi(count)
			if err != nil {
				return err
			}
			count += s[i : i+1]
			switch s[i] {
			case 'w':
				count = fmt.Sprintf("%dh", n*24*7)
			case 'd':
				count = fmt.Sprintf("%dh", n*24)
			}
			d, err := time.ParseDuration(count)
			if err != nil {
				return err
			}
			ttl += d
			count = ""
		} else {
			count += s[i : i+1]
		}
	}
	if count != "" {
		return errors.New(count)
	}
	return nil
}
