package orm

import (
	"fmt"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo/options"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestNew(t *testing.T) {
	initTest()
	is := iti_single()
	doc := Create(is).(*story)
	assert.Equal(t, is.Title, doc.Title)
	assert.Equal(t, is.Chapters[0].Summary, doc.Chapters[0].Summary)
}

func TestSave(t *testing.T) {
	initTest()
	storyDoc := Create(iti_multi()).(*story)
	lauthor := Create(author).(*user)
	storyDoc.Author = lauthor
	assert.Equal(t, storyDoc.Id(), int64(0))
	assert.Equal(t, lauthor.ID, storyDoc.Author.ID)
	aerr := lauthor.Save()
	assert.Equal(t, nil, aerr)
	serr := storyDoc.Save()
	assert.Equal(t, nil, serr)
	assert.Less(t, int64(0), storyDoc.ID)
	assert.Less(t, int64(0), lauthor.ID)
	for _, c := range storyDoc.Chapters {
		assert.NotZero(t, c.ChapterID)
	}
}

func TestPopulate(t *testing.T) {
	initTest()

	bandDoc := Create(iti_single().Chapters[0].Bands[0]).(*band)
	storyDoc := Create(iti_single()).(*story)
	mauthor := Create(author).(*user)
	saveDoc(t, mauthor)
	saveDoc(t, bandDoc)
	storyDoc.Author = mauthor
	saveDoc(t, storyDoc)
	assert.Greater(t, storyDoc.ID, int64(0))

	smodel := ModelRegistry["story"]
	q, err := smodel.FindByID(storyDoc.ID)
	assert.Equal(t, nil, err)
	assert.NotPanics(t, func() {
		foundDoc := &story{}
		q.Populate("Author", "Chapters.Bands").Exec(foundDoc)
		j, _ := q.JSON()
		fmt.Printf("%s\n", j)
	})
	for _, c := range storyDoc.Chapters {
		assert.NotZero(t, c.Bands[0].Name)
	}
}

func TestUpdate(t *testing.T) {
	initTest()
	nb := Create(metallica).(*band)
	saveDoc(t, nb)
	nb.Locked = true
	saveDoc(t, nb)

	foundM := ModelRegistry["band"]
	q, err := foundM.FindByID(int64(1))
	assert.Equal(t, nil, err)
	found := &band{}
	q.Exec(found)
	assert.Equal(t, int64(1), found.ID)
	assert.Equal(t, nil, err)
	assert.Equal(t, true, found.Locked)
}

func TestModel_FindAll(t *testing.T) {
	initTest()
	im := iti_multi()
	createAndSave(t, &im)
	smodel := ModelRegistry["story"]
	query, err := smodel.Find(bson.M{}, options.Find())
	assert.Equal(t, nil, err)
	final := CreateSlice(story{})
	query.Exec(&final)
	assert.Greater(t, len(final), 0)
}

func TestModel_PopulateMulti(t *testing.T) {
	initTest()
	bandDoc := Create(iti_single().Chapters[0].Bands[0]).(*band)
	saveDoc(t, bandDoc)
	mauthor := Create(author).(*user)
	saveDoc(t, mauthor)
	im := iti_multi()
	im.Author = mauthor
	createAndSave(t, &im)
	smodel := ModelRegistry["story"]
	query, err := smodel.Find(bson.M{}, options.Find())
	assert.Equal(t, nil, err)
	final := CreateSlice(story{})
	query.Populate("Author", "Chapters.Bands").Exec(&final)
	assert.Greater(t, len(final), 0)
	for _, s := range final {
		assert.NotZero(t, s.Chapters[0].Bands[0].Name)
	}
}

func TestModel_PopulateChained_Multi(t *testing.T) {
	initTest()
	im := iti_multi()
	bandDoc := Create(iti_single().Chapters[0].Bands[0]).(*band)
	saveDoc(t, bandDoc)
	mauthor := Create(author).(*user)
	saveDoc(t, mauthor)
	im.Author = mauthor
	createAndSave(t, &im)
	smodel := ModelRegistry["story"]
	query, err := smodel.Find(bson.M{}, options.Find())
	assert.Equal(t, nil, err)
	final := CreateSlice(story{})
	query.Populate("Author").Populate("Chapters.Bands").Exec(&final)
	assert.Greater(t, len(final), 0)
	for _, s := range final {
		assert.NotZero(t, s.Chapters[0].Bands[0].Name)
	}
}

func TestPopulate_Chained(t *testing.T) {
	initTest()

	bandDoc := Create(iti_single().Chapters[0].Bands[0]).(*band)
	storyDoc := Create(iti_single()).(*story)
	mauthor := Create(author).(*user)
	saveDoc(t, mauthor)
	saveDoc(t, bandDoc)
	storyDoc.Author = mauthor
	saveDoc(t, storyDoc)
	assert.Greater(t, storyDoc.ID, int64(0))

	smodel := ModelRegistry["story"]
	q, err := smodel.FindByID(storyDoc.ID)
	assert.Equal(t, nil, err)
	assert.NotPanics(t, func() {
		foundDoc := &story{}
		q.Populate("Author").Populate("Chapters.Bands").Exec(foundDoc)
		j, _ := q.JSON()
		fmt.Printf("%s\n", j)
	})
	for _, c := range storyDoc.Chapters {
		assert.NotZero(t, c.Bands[0].Name)
	}
}

func TestModel_Append(t *testing.T) {
	initTest()
	bandDoc := Create(metallica).(*band)
	saveDoc(t, bandDoc)
	bmodel := ModelRegistry["band"]
	query, err := bmodel.FindByID(int64(1))
	assert.Equal(t, nil, err)
	fin := &band{}
	query.Exec(fin)
	assert.Greater(t, fin.ID, int64(0))
	err = fin.Append("Characters", "Robert Trujillo")
	assert.Equal(t, nil, err)
	saveDoc(t, fin)
	fin = &band{}
	query, _ = bmodel.FindByID(int64(1))
	query.Exec(fin)
	assert.Greater(t, len(fin.Characters), 4)
}

func TestModel_Delete(t *testing.T) {
	initTest()
	bandDoc := Create(metallica).(*band)
	saveDoc(t, bandDoc)
	err := bandDoc.Delete()
	assert.Nil(t, err)
}

func TestModel_Pull(t *testing.T) {
	initTest()
	storyDoc := Create(iti_multi()).(*story)
	smodel := ModelRegistry["story"]
	saveDoc(t, storyDoc)
	err := storyDoc.Pull("Chapters", storyDoc.Chapters[4])
	assert.Nil(t, err)
	assert.NotZero(t, storyDoc.ID)
	saveDoc(t, storyDoc)
	fin := &story{}
	query, err := smodel.FindByID(storyDoc.ID)
	assert.Nil(t, err)
	query.Exec(fin)
	assert.Equal(t, 4, len(fin.Chapters))
}

func TestModel_Swap(t *testing.T) {
	initTest()
	is := iti_single()
	is.Author = &author
	storyDoc := Create(iti_single()).(*story)
	saveDoc(t, storyDoc)
	storyDoc.Chapters[0].Bands = append(storyDoc.Chapters[0].Bands, bodom)
	assert.Equal(t, 2, len(storyDoc.Chapters[0].Bands))
	err := storyDoc.Swap("Chapters[0].Bands", 0, 1)
	assert.Nil(t, err)
	c := storyDoc.Chapters[0].Bands
	assert.Equal(t, bodom.ID, c[0].ID)
	assert.Equal(t, diamondHead.ID, c[1].ID)
	saveDoc(t, storyDoc)
}

func TestModel_GridFSLoad(t *testing.T) {
	initTest()
	ModelRegistry.Model(somethingWithNestedChapters{})
	model := ModelRegistry["somethingWithNestedChapters"]
	thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters)
	found := &somethingWithNestedChapters{}

	saveDoc(t, thingDoc)
	assert.NotZero(t, thingDoc.ID)
	fq, err := model.FindByID(thingDoc.ID)
	assert.Nil(t, err)
	fq.LoadFile("NestedText", "Chapters.Text").Exec(found)
	assert.NotZero(t, found.NestedText)
	assert.NotZero(t, len(found.Chapters))
	for _, c := range found.Chapters {
		assert.NotZero(t, c.Text)
	}
}

func TestModel_GridFSLoad_Chained(t *testing.T) {
	initTest()
	ModelRegistry.Model(somethingWithNestedChapters{})
	model := ModelRegistry["somethingWithNestedChapters"]
	thingDoc := Create(doSomethingWithNested()).(*somethingWithNestedChapters)
	found := &somethingWithNestedChapters{}

	saveDoc(t, thingDoc)
	assert.NotZero(t, thingDoc.ID)
	fq, err := model.FindByID(thingDoc.ID)
	assert.Nil(t, err)
	fq.LoadFile("NestedText").LoadFile("Chapters.Text").Exec(found)
	assert.NotZero(t, found.NestedText)
	assert.NotZero(t, len(found.Chapters))
	for _, c := range found.Chapters {
		assert.NotZero(t, c.Text)
	}
}

func TestModel_GridFSLoad_Complex(t *testing.T) {
	initTest()
	model := ModelRegistry["story"]
	bandDoc := Create(iti_single().Chapters[0].Bands[0]).(*band)
	thingDoc := Create(iti_multi()).(*story)
	mauthor := Create(author).(*user)
	found := &story{}
	saveDoc(t, bandDoc)
	saveDoc(t, mauthor)
	thingDoc.Author = mauthor
	saveDoc(t, thingDoc)
	assert.NotZero(t, thingDoc.ID)
	fq, err := model.FindByID(thingDoc.ID)
	assert.Nil(t, err)
	fq.Populate("Author", "Chapters.Bands").LoadFile("Chapters.Text").Exec(found)
	assert.NotZero(t, len(found.Chapters))
	for _, c := range found.Chapters {
		assert.NotZero(t, c.Text)
		assert.NotZero(t, c.Bands[0].Name)
	}
	j, _ := fq.JSON()
	fmt.Printf("%s\n", j)
}