package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"time"
)

const (
	leetCodeJSON = "leetcode.json"
)

type leetcode struct {
	Username string    // 用户名
	Ranking  int       // 网站排名
	Updated  time.Time // 数据更新时间

	Record   record   // 已解答题目与全部题目的数量,按照难度统计
	Problems problems // 所有问题的集合
}

func newLeetCode() *leetcode {
	log.Println("开始,获取 LeetCode 数据")

	lc, err := readLeetCode()
	if err != nil {
		log.Println("读取 LeetCode 的记录失败,正在重新生成 LeetCode 记录。失败原因:", err.Error())
		lc = getLeetCode()
	}

	lc.refresh()

	lc.save()

	log.Println("获取 LeetCode 的最新数据")
	return lc
}

func readLeetCode() (*leetcode, error) {
	data, err := ioutil.ReadFile(leetCodeJSON)
	if err != nil {
		return nil, errors.New("读取文件失败:" + err.Error())
	}

	lc := new(leetcode)
	if err := json.Unmarshal(data, lc); err != nil {
		return nil, errors.New("转换成 leetcode 时,失败:" + err.Error())
	}

	return lc, nil
}

func (lc *leetcode) save() {

	if err := os.Remove(leetCodeJSON); err != nil {
		log.Panicf("删除 %s 失败,原因是:%s", leetCodeJSON, err)
	}

	raw, err := json.MarshalIndent(lc, "", "\t")
	if err != nil {
		log.Fatal("无法把Leetcode数据转换成[]bytes: ", err)
	}
	if err = ioutil.WriteFile(leetCodeJSON, raw, 0666); err != nil {
		log.Fatal("无法把 Marshal 后的 lc 保存到文件: ", err)
	}
	log.Println("最新的 LeetCode 记录已经保存。")
	return
}

func (lc *leetcode) refresh() {
	if time.Since(lc.Updated) < time.Minute {
		log.Printf("LeetCode 数据在 %s 前刚刚更新过,跳过此次刷新\n", time.Since(lc.Updated))
		return
	}

	log.Println("开始,刷新 LeetCode 数据")
	newLC := getLeetCode()

	logDiff(lc, newLC)

	*lc = *newLC
}

func logDiff(old, new *leetcode) {
	// 对比 ranking
	str := fmt.Sprintf("当前排名 %d", new.Ranking)
	verb, delta := "进步", old.Ranking-new.Ranking
	if new.Ranking > old.Ranking {
		verb, delta = "后退", new.Ranking-old.Ranking
	}
	str += fmt.Sprintf(",%s了 %d 名", verb, delta)
	log.Println(str)

	lenOld, lenNew := len(old.Problems), len(new.Problems)
	hasNewFinished := false

	i := 0

	// 检查新旧都有的问题
	for i < lenOld && i < lenNew {
		o, n := old.Problems[i], new.Problems[i]
		// 检查是 n 是否是新 完成
		if o.IsAccepted == false && n.IsAccepted == true {
			log.Printf("~新完成~ %d - %s", n.ID, n.Title)
			dida("re", n)
			hasNewFinished = true
		}
		// 检查是 n 是否是新 收藏
		if o.IsFavor == false && n.IsFavor == true {
			log.Printf("~新收藏~ %d - %s", n.ID, n.Title)
			dida("fa", n)
		} else if o.IsFavor == true && n.IsFavor == false {
			log.Printf("~取消收藏~ %d - %s", o.ID, o.Title)
			time.Sleep(time.Second)
		}

		// 有时候,会在中间添加新题
		if o.Title == "" && n.Title != "" {
			log.Printf("新题: %d - %s", new.Problems[i].ID, new.Problems[i].Title)
			dida("do", n)
		}

		i++
	}

	log.Printf("已经检查完了 %d 题\n", i)

	if !hasNewFinished {
		log.Println("~ 没有新完成习题 ~")
	}

	// 检查新添加的习题
	for i < lenNew {
		if new.Problems[i].isAvailable() {
			log.Printf("新题: %d - %s", new.Problems[i].ID, new.Problems[i].Title)
			dida("do", new.Problems[i])
		}
		i++
	}
}

func (lc *leetcode) ProgressTable() string {
	return lc.Record.progressTable()
}

func (lc *leetcode) AvailableTable() string {
	return lc.Problems.available().table()
}

func (lc *leetcode) FavoriteTable() string {
	return lc.Problems.favorite().table()
}

func (lc *leetcode) FavoriteCount() int {
	return len(lc.Problems.favorite())
}

func (lc *leetcode) UnavailableList() string {
	res := lc.Problems.unavailable().list()
	// 为了 README.md 文档的美观,需要删除最后一个换行符号
	return res[:len(res)-1]
}