package mapping

import (
	"encoding/json"
	"reflect"
	"testing"
	"time"

	"github.com/moustachioed/dash/backend/dashapi"
	"github.com/moustachioed/dash/backend/database/models"
	"gorm.io/datatypes"
)

func TestJournalApiToDbFullObject(t *testing.T) {
	mapper := NewMapperImpl()

	api := dashapi.JournalEntry{
		Date:           "2022-02-18",
		Thankful:       []string{"ttest1", "ttest2", "ttest3"},
		LookingForward: []string{"lftest1", "lftest2", "lftest3"},
		BeenGreat:      []string{"bgtest1", "bgtest2", "bgtest3"},
		DoBetter:       []string{"dbtest1", "dbtest2", "dbtest3"},
		Journal:        "jtest",
	}

	db := mapper.JournalApiToDs(api).(models.Journal)

	gotDate, _ := db.Date.Value()
	got := gotDate.(time.Time).Format("2006-01-02")
	wantDate := api.Date

	if got != wantDate {
		t.Errorf("Mapped string %s not equal want string %s", got, wantDate)
	}

	got = db.Thankful.String()
	want, _ := json.Marshal(api.Thankful)

	if got != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got = db.LookingForward.String()
	want, _ = json.Marshal(api.LookingForward)

	if got != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got = db.BeenGreat.String()
	want, _ = json.Marshal(api.BeenGreat)

	if got != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got = db.DoBetter.String()
	want, _ = json.Marshal(api.DoBetter)

	if got != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got = db.Journal
	wantstring := api.Journal

	if got != string(wantstring) {
		t.Errorf("Mapped string %s not equal want string %s", got, wantstring)
	}
}

func TestJournalApiToDbPartialObject(t *testing.T) {
	mapper := NewMapperImpl()

	api := dashapi.JournalEntry{
		Date:           "2022-02-18",
		Thankful:       nil,
		LookingForward: []string{"lftest1", "lftest2", "lftest3"},
		BeenGreat:      nil,
		DoBetter:       []string{"dbtest1", "dbtest2", "dbtest3"},
		Journal:        "",
	}

	db := mapper.JournalApiToDs(api).(models.Journal)

	gotDate, _ := db.Date.Value()
	got := gotDate.(time.Time).Format("2006-01-02")
	wantDate := api.Date

	if got != wantDate {
		t.Errorf("Mapped string %s not equal want string %s", got, wantDate)
	}

	got = db.Thankful.String()
	want, _ := json.Marshal(api.Thankful)

	if got != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got = db.LookingForward.String()
	want, _ = json.Marshal(api.LookingForward)

	if got != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got = db.BeenGreat.String()
	want, _ = json.Marshal(api.BeenGreat)

	if got != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got = db.DoBetter.String()
	want, _ = json.Marshal(api.DoBetter)

	if got != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got = db.Journal
	wantstring := api.Journal

	if got != string(wantstring) {
		t.Errorf("Mapped string %s not equal want string %s", got, wantstring)
	}
}

func TestJournalDbToApiFullObject(t *testing.T) {
	mapper := NewMapperImpl()

	date, _ := time.Parse("2006-01-02", "2022-02-18")
	thankful, _ := json.Marshal([]string{"ttest1", "ttest2", "ttest3"})
	lookingForward, _ := json.Marshal([]string{"lftest1", "lftest2", "lftest3"})
	beenGreat, _ := json.Marshal([]string{"bgtest1", "bgtest2", "bgtest3"})
	doBetter, _ := json.Marshal([]string{"dbtest1", "dbtest2", "test3"})
	journal := "jtest"

	db := models.Journal{
		Date:           datatypes.Date(date),
		Thankful:       datatypes.JSON(thankful),
		LookingForward: datatypes.JSON(lookingForward),
		BeenGreat:      datatypes.JSON(beenGreat),
		DoBetter:       datatypes.JSON(doBetter),
		Journal:        journal,
	}

	api := mapper.JournalDsToApi(db).(dashapi.JournalEntry)

	got, _ := json.Marshal(api.Date)
	wantDate := "\"2022-02-18\""

	if string(got) != wantDate {
		t.Errorf("Mapped string %s not equal want string %s", got, wantDate)
	}

	got, _ = json.Marshal(api.Thankful)
	want := thankful

	if string(got) != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got, _ = json.Marshal(api.LookingForward)
	want = lookingForward

	if string(got) != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got, _ = json.Marshal(api.BeenGreat)
	want = beenGreat

	if string(got) != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got, _ = json.Marshal(api.DoBetter)
	want = doBetter

	if string(got) != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	gotJournal := api.Journal
	wantJournal := journal

	if gotJournal != wantJournal {
		t.Errorf("Mapped string %s not equal want string %s", gotJournal, wantJournal)
	}
}

func TestJournalDbToApiPartialObject(t *testing.T) {
	mapper := NewMapperImpl()

	date, _ := time.Parse("2006-01-02", "2022-02-18")
	var thankful []byte = nil
	lookingForward, _ := json.Marshal([]string{"lftest1", "lftest2", "lftest3"})
	var beenGreat []byte = nil
	doBetter, _ := json.Marshal([]string{"dbtest1", "dbtest2", "test3"})
	journal := "jtest"

	db := models.Journal{
		Date:           datatypes.Date(date),
		Thankful:       datatypes.JSON(thankful),
		LookingForward: datatypes.JSON(lookingForward),
		BeenGreat:      datatypes.JSON(beenGreat),
		DoBetter:       datatypes.JSON(doBetter),
		Journal:        journal,
	}

	api := mapper.JournalDsToApi(db).(dashapi.JournalEntry)

	got, _ := json.Marshal(api.Date)
	wantDate := "\"2022-02-18\""

	if string(got) != wantDate {
		t.Errorf("Mapped string %s not equal want string %s", got, wantDate)
	}

	got, _ = json.Marshal(api.Thankful)
	wantString := "null"

	if string(got) != wantString {
		t.Errorf("Mapped string %s not equal want string %s", got, wantString)
	}

	got, _ = json.Marshal(api.LookingForward)
	want := lookingForward

	if string(got) != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	got, _ = json.Marshal(api.BeenGreat)
	wantString = "null"

	if string(got) != wantString {
		t.Errorf("Mapped string %s not equal want string %s", got, wantString)
	}

	got, _ = json.Marshal(api.DoBetter)
	want = doBetter

	if string(got) != string(want) {
		t.Errorf("Mapped string %s not equal want string %s", got, want)
	}

	gotJournal := api.Journal
	wantJournal := journal

	if gotJournal != wantJournal {
		t.Errorf("Mapped string %s not equal want string %s", gotJournal, wantJournal)
	}
}

func TestMapperImpl_PlanDayApiToDs(t *testing.T) {
	date, _ := time.Parse("2006-01-02", "2022-02-18")
	morning, _ := json.Marshal([]string{"motest1", "motest2", "motest3"})
	midday, _ := json.Marshal([]string{"mitest1", "mitest2", "mitest3"})
	afternoon, _ := json.Marshal([]string{"antest1", "antest2", "antest3"})
	evening, _ := json.Marshal([]string{"etest1", "etest2", "etest3"})
	pleasant, _ := json.Marshal([]string{"ptest1", "ptest2", "ptest3"})
	reminders, _ := json.Marshal([]string{"rtest1", "rtest2", "rtest3"})
	empty, _ := json.Marshal(nil)

	type args struct {
		am interface{}
	}
	tests := []struct {
		name   string
		mapper MapperImpl
		args   args
		want   interface{}
	}{
		{
			name:   "Full Object",
			mapper: MapperImpl{},
			args: args{
				am: dashapi.PlanDay{
					Date:      "2022-02-18",
					Morning:   []string{"motest1", "motest2", "motest3"},
					Midday:    []string{"mitest1", "mitest2", "mitest3"},
					Afternoon: []string{"antest1", "antest2", "antest3"},
					Evening:   []string{"etest1", "etest2", "etest3"},
					Pleasant:  []string{"ptest1", "ptest2", "ptest3"},
					Reminders: []string{"rtest1", "rtest2", "rtest3"},
				}},
			want: models.PlanDay{
				Date:      datatypes.Date(date),
				Morning:   datatypes.JSON(morning),
				Midday:    datatypes.JSON(midday),
				Afternoon: datatypes.JSON(afternoon),
				Evening:   datatypes.JSON(evening),
				Pleasant:  datatypes.JSON(pleasant),
				Reminders: datatypes.JSON(reminders),
			},
		},
		{
			name:   "Partial Object",
			mapper: MapperImpl{},
			args: args{
				am: dashapi.PlanDay{
					Date:      "2022-02-18",
					Morning:   []string{"motest1", "motest2", "motest3"},
					Midday:    nil,
					Afternoon: nil,
					Evening:   nil,
					Pleasant:  []string{"ptest1", "ptest2", "ptest3"},
					Reminders: nil,
				}},
			want: models.PlanDay{
				Date:      datatypes.Date(date),
				Morning:   datatypes.JSON(morning),
				Midday:    datatypes.JSON(empty),
				Afternoon: datatypes.JSON(empty),
				Evening:   datatypes.JSON(empty),
				Pleasant:  datatypes.JSON(pleasant),
				Reminders: datatypes.JSON(empty),
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mapper := MapperImpl{}
			if got := mapper.PlanDayApiToDs(tt.args.am); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("MapperImpl.PlanDayApiToDs() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestMapperImpl_PlanDayDsToApi(t *testing.T) {
	date, _ := time.Parse("2006-01-02", "2022-02-18")
	morning, _ := json.Marshal([]string{"motest1", "motest2", "motest3"})
	midday, _ := json.Marshal([]string{"mitest1", "mitest2", "mitest3"})
	afternoon, _ := json.Marshal([]string{"antest1", "antest2", "antest3"})
	evening, _ := json.Marshal([]string{"etest1", "etest2", "etest3"})
	pleasant, _ := json.Marshal([]string{"ptest1", "ptest2", "ptest3"})
	reminders, _ := json.Marshal([]string{"rtest1", "rtest2", "rtest3"})
	empty, _ := json.Marshal(nil)

	type args struct {
		dm interface{}
	}
	tests := []struct {
		name   string
		mapper MapperImpl
		args   args
		want   interface{}
	}{
		{
			name:   "Full Object",
			mapper: MapperImpl{},
			args: args{
				dm: models.PlanDay{
					Date:      datatypes.Date(date),
					Morning:   datatypes.JSON(morning),
					Midday:    datatypes.JSON(midday),
					Afternoon: datatypes.JSON(afternoon),
					Evening:   datatypes.JSON(evening),
					Pleasant:  datatypes.JSON(pleasant),
					Reminders: datatypes.JSON(reminders),
				}},
			want: dashapi.PlanDay{
				Date:      "2022-02-18",
				Morning:   []string{"motest1", "motest2", "motest3"},
				Midday:    []string{"mitest1", "mitest2", "mitest3"},
				Afternoon: []string{"antest1", "antest2", "antest3"},
				Evening:   []string{"etest1", "etest2", "etest3"},
				Pleasant:  []string{"ptest1", "ptest2", "ptest3"},
				Reminders: []string{"rtest1", "rtest2", "rtest3"},
			},
		},
		{
			name:   "Partial Object",
			mapper: MapperImpl{},
			args: args{
				dm: models.PlanDay{
					Date:      datatypes.Date(date),
					Morning:   datatypes.JSON(morning),
					Midday:    datatypes.JSON(empty),
					Afternoon: datatypes.JSON(afternoon),
					Evening:   datatypes.JSON(empty),
					Pleasant:  datatypes.JSON(pleasant),
					Reminders: datatypes.JSON(empty),
				}},
			want: dashapi.PlanDay{
				Date:      "2022-02-18",
				Morning:   []string{"motest1", "motest2", "motest3"},
				Midday:    nil,
				Afternoon: []string{"antest1", "antest2", "antest3"},
				Evening:   nil,
				Pleasant:  []string{"ptest1", "ptest2", "ptest3"},
				Reminders: nil,
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mapper := MapperImpl{}
			if got := mapper.PlanDayDsToApi(tt.args.dm); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("MapperImpl.PlanDayDsToApi() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestMapperImpl_PlanWeekApiToDs(t *testing.T) {
	date, _ := time.Parse("2006-01-02", "2022-02-18")
	items, _ := json.Marshal([]dashapi.PlanWeekItem{
		{
			Item:    "test1",
			NumTodo: 3,
			NumDone: 1,
		},
		{
			Item:    "test2",
			NumTodo: 3,
			NumDone: 3,
		}})
	empty, _ := json.Marshal(nil)

	type args struct {
		am interface{}
	}
	tests := []struct {
		name   string
		mapper MapperImpl
		args   args
		wantDm interface{}
	}{
		{
			name:   "Full Object",
			mapper: MapperImpl{},
			args: args{
				am: dashapi.PlanWeek{
					Date: "2022-02-18",
					Items: []dashapi.PlanWeekItem{
						{
							Item:    "test1",
							NumTodo: 3,
							NumDone: 1,
						},
						{
							Item:    "test2",
							NumTodo: 3,
							NumDone: 3,
						},
					}}},
			wantDm: models.PlanWeek{
				Date:  datatypes.Date(date),
				Items: datatypes.JSON(items),
			},
		},
		{
			name:   "Empty Object",
			mapper: MapperImpl{},
			args: args{
				am: dashapi.PlanWeek{
					Date:  "2022-02-18",
					Items: nil,
				}},
			wantDm: models.PlanWeek{
				Date:  datatypes.Date(date),
				Items: datatypes.JSON(empty),
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mapper := MapperImpl{}
			if gotDm := mapper.PlanWeekApiToDs(tt.args.am); !reflect.DeepEqual(gotDm, tt.wantDm) {
				t.Errorf("MapperImpl.PlanWeekApiToDs() = %v, want %v", gotDm, tt.wantDm)
			}
		})
	}
}

// write a test for PlanMonthApiToDs

func TestMapperImpl_PlanWeekDsToApi(t *testing.T) {
	date, _ := time.Parse("2006-01-02", "2022-02-18")
	items, _ := json.Marshal([]dashapi.PlanWeekItem{
		{
			Item:    "test1",
			NumTodo: 3,
			NumDone: 1,
		},
		{
			Item:    "test2",
			NumTodo: 3,
			NumDone: 3,
		}})
	empty, _ := json.Marshal(nil)

	type args struct {
		dm interface{}
	}
	tests := []struct {
		name   string
		mapper MapperImpl
		args   args
		wantAm interface{}
	}{
		{
			name:   "Full Object",
			mapper: MapperImpl{},
			args: args{
				dm: models.PlanWeek{
					Date:  datatypes.Date(date),
					Items: datatypes.JSON(items),
				}},
			wantAm: dashapi.PlanWeek{
				Date: "2022-02-18",
				Items: []dashapi.PlanWeekItem{
					{
						Item:    "test1",
						NumTodo: 3,
						NumDone: 1,
					},
					{
						Item:    "test2",
						NumTodo: 3,
						NumDone: 3,
					}},
			},
		},
		{
			name:   "Empty Object",
			mapper: MapperImpl{},
			args: args{
				dm: models.PlanWeek{
					Date:  datatypes.Date(date),
					Items: datatypes.JSON(empty),
				}},
			wantAm: dashapi.PlanWeek{
				Date:  "2022-02-18",
				Items: nil,
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mapper := MapperImpl{}
			if gotAm := mapper.PlanWeekDsToApi(tt.args.dm); !reflect.DeepEqual(gotAm, tt.wantAm) {
				t.Errorf("MapperImpl.PlanWeekDsToApi() = %v, want %v", gotAm, tt.wantAm)
			}
		})
	}
}

func TestMapperImpl_StringToDate(t *testing.T) {
	type args struct {
		dateString string
	}
	tests := []struct {
		name    string
		mapper  MapperImpl
		args    args
		want    time.Time
		wantErr bool
	}{
		{
			name:   "Well-formed date",
			mapper: MapperImpl{},
			args: args{
				dateString: "1987-02-18",
			},
			want:    time.Date(1987, 2, 18, 0, 0, 0, 0, time.UTC),
			wantErr: false,
		},
		{
			name:   "False string",
			mapper: MapperImpl{},
			args: args{
				dateString: "1987-02-18'T'13:02:18",
			},
			want:    time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mapper := MapperImpl{}
			got, err := mapper.StringToDate(tt.args.dateString)
			if (err != nil) != tt.wantErr {
				t.Errorf("MapperImpl.StringToDate() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("MapperImpl.StringToDate() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestMapperImpl_DateToString(t *testing.T) {
	type args struct {
		date time.Time
	}
	tests := []struct {
		name   string
		mapper MapperImpl
		args   args
		want   string
	}{
		{
			name:   "Well-formed date",
			mapper: MapperImpl{},
			args: args{
				date: time.Date(1987, 2, 18, 0, 0, 0, 0, time.UTC),
			},
			want: "1987-02-18",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mapper := MapperImpl{}
			if got := mapper.DateToString(tt.args.date); got != tt.want {
				t.Errorf("MapperImpl.DateToString() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestMapperImpl_InboxApiToDs(t *testing.T) {
	type args struct {
		am interface{}
	}
	tests := []struct {
		name   string
		mapper MapperImpl
		args   args
		wantDm interface{}
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mapper := MapperImpl{}
			if gotDm := mapper.InboxApiToDs(tt.args.am); !reflect.DeepEqual(gotDm, tt.wantDm) {
				t.Errorf("MapperImpl.InboxApiToDs() = %v, want %v", gotDm, tt.wantDm)
			}
		})
	}
}
