package pttodo

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"testing"
	"time"

	yaml "gopkg.in/yaml.v2"
)

func TestJSONRoot(t *testing.T) {
	root := Root{
		Todo:      []Todo{Todo{Todo: "todo", Details: "detail", Schedule: "teehee", TS: 1}},
		Scheduled: []Todo{Todo{Todo: "scheduled", Details: "detail", Schedule: "teehee", TS: 1}},
		Done:      []Todo{Todo{Todo: "done", Details: "detail", Schedule: "teehee", TS: 1}},
	}
	var root2 Root
	if b, err := json.Marshal(root); err != nil {
		t.Fatal(err)
	} else if err := json.Unmarshal(b, &root2); err != nil {
		t.Fatal(err)
	} else if fmt.Sprint(root) != fmt.Sprint(root2) {
		t.Fatal(root, root2)
	} else if !root.Equals(root2) {
		t.Fatal(root2)
	}
}

func TestRootMoveScheduledToTodo(t *testing.T) {
	scheduleFuture := Schedule(strconv.FormatInt(time.Now().Unix()+100, 10))
	schedulePast := Schedule(strconv.FormatInt(time.Now().Unix()-100, 10))
	cases := map[string]struct {
		in   Root
		want Root
	}{
		"empty": {},
		"no scheduled": {
			in:   Root{Todo: []Todo{Todo{}}},
			want: Root{Todo: []Todo{Todo{}}},
		},
		"one scheduled not ready": {
			in:   Root{Scheduled: []Todo{Todo{Schedule: scheduleFuture}}},
			want: Root{Scheduled: []Todo{Todo{Schedule: scheduleFuture}}},
		},
		"one scheduled ready": {
			in:   Root{Scheduled: []Todo{Todo{Schedule: schedulePast}}},
			want: Root{Todo: []Todo{Todo{Schedule: schedulePast}}},
		},
		"two scheduled one ready one not": {
			in: Root{
				Scheduled: []Todo{
					Todo{Schedule: schedulePast},
					Todo{Schedule: scheduleFuture},
				},
			},
			want: Root{
				Todo: []Todo{
					Todo{Schedule: schedulePast},
				},
				Scheduled: []Todo{
					Todo{Schedule: scheduleFuture},
				},
			},
		},
		"two scheduled one not one ready": {
			in: Root{
				Scheduled: []Todo{
					Todo{Schedule: scheduleFuture},
					Todo{Schedule: schedulePast},
				},
			},
			want: Root{
				Todo: []Todo{
					Todo{Schedule: schedulePast},
				},
				Scheduled: []Todo{
					Todo{Schedule: scheduleFuture},
				},
			},
		},
	}

	for name, d := range cases {
		c := d
		t.Run(name, func(t *testing.T) {
			inB, _ := json.Marshal(c.in)
			var got Root
			json.Unmarshal(inB, &got)

			wantB, _ := json.Marshal(c.want)
			var want Root
			json.Unmarshal(wantB, &want)

			got.MoveScheduledToTodo()
			if fmt.Sprintf("%+v", got) != fmt.Sprintf("%+v", want) {
				t.Fatalf("want \n\t%+v, got \n\t%+v", want, got)
			} else if !got.Equals(want) {
				t.Fatalf("want \n\t%+v, got \n\t%+v", want, got)
			}
		})
	}
}

func TestMergeRoots(t *testing.T) {
	root0yaml := `
todo:
- a
- b
- todo: c
- todo: d
  tags: a
- exclusive to 0
   `
	root1yaml := `
todo:
- a
- b
- todo: c
- todo: d
  tags: b
- exclusive to 1
   `
	rootWantyaml := `
todo:
- a
- b
- todo: c
- todo: d
  tags: a
- exclusive to 0
- exclusive to 1
   `

	var root0, root1, rootWant Root
	if err := yaml.Unmarshal([]byte(root0yaml), &root0); err != nil {
		t.Fatal(err)
	}
	if err := yaml.Unmarshal([]byte(root1yaml), &root1); err != nil {
		t.Fatal(err)
	}
	if err := yaml.Unmarshal([]byte(rootWantyaml), &rootWant); err != nil {
		t.Fatal(err)
	}

	root0.MergeIn(root1)
	if fmt.Sprintf("%+v", root0) != fmt.Sprintf("%+v", rootWant) {
		t.Fatalf("want \n\t%+v, got \n\t%+v", rootWant, root0)
	} else if !root0.Equals(rootWant) {
		t.Fatalf("want \n\t%+v, got \n\t%+v", rootWant, root0)
	}
}

func TestRootMarshalYAMLWriteTS(t *testing.T) {
	root := Root{
		Todo:      []Todo{Todo{Todo: "todo", TS: 1, writeTS: true}},
		Scheduled: []Todo{Todo{Todo: "sched", TS: 2, writeTS: false, Schedule: "2099-01-01"}},
		Done:      []Todo{Todo{Todo: "done", TS: 3, writeTS: false}},
	}
	got, err := yaml.Marshal(root)
	if err != nil {
		t.Fatal(err)
	}
	got = bytes.TrimSpace(got)
	want := strings.TrimSpace(`
todo:
- todo
scheduled:
- todo: sched
  schedule: "2099-01-01"
  ts: Wed Dec 31 17:00:02 MST 1969
done:
- todo: done
  ts: Wed Dec 31 17:00:03 MST 1969
   `)
	if string(got) != want {
		t.Fatalf("want\n\t%q, got\n\t%q", want, string(got))
	}
}
