package web

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http/httptest"
	"testing"
	"time"

	"dyproxy/.gen/model"
	"dyproxy/.gen/table"
	"dyproxy/providers/db"

	"github.com/go-faker/faker/v4"
	. "github.com/go-jet/jet/v2/sqlite"
	"github.com/rogeecn/fabfile"
	"github.com/sirupsen/logrus"
	. "github.com/smartystreets/goconvey/convey"
	"github.com/stretchr/testify/suite"
)

type WebTestSuite struct {
	suite.Suite

	web *WebServer
}

func TestWebServer(t *testing.T) {
	db, err := db.Connect(fabfile.MustFind("data.db"))
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()

	app := New(
		WithDB(db),
		WithLogger(),
		WithRecover(),
		WithRoutes(),
		WithPendingCleaner(),
	)

	suite.Run(t, &WebTestSuite{web: app})
}

func (s *WebTestSuite) Test_index() {
	Convey("Test_index", s.T(), func() {
		req := httptest.NewRequest("GET", "http://localhost/", nil)

		resp, err := s.web.engine.Test(req)
		So(err, ShouldBeNil)

		defer resp.Body.Close()

		body, err := io.ReadAll(resp.Body)
		So(err, ShouldBeNil)

		s.T().Logf("BODY: %s", body)
	})
}

func (s *WebTestSuite) Test_css() {
	Convey("Test_css", s.T(), func() {
		req := httptest.NewRequest("GET", "http://localhost/style.css", nil)

		resp, err := s.web.engine.Test(req)
		So(err, ShouldBeNil)

		defer resp.Body.Close()

		body, err := io.ReadAll(resp.Body)
		So(err, ShouldBeNil)

		s.T().Logf("BODY: %s", body)
	})
}

func (s *WebTestSuite) Test_Expert() {
	Convey("Expert", s.T(), func() {
		Convey("GET", func() {
			t := table.Expert
			db.Truncate(s.web.db, t.TableName())

			var m model.Expert
			So(faker.FakeData(&m), ShouldBeNil)
			m.Since = 0
			m.UID = "110"
			_, err := t.INSERT().MODEL(m).Exec(s.web.db)
			So(err, ShouldBeNil)

			req := httptest.NewRequest("GET", "http://localhost/api/experts/110", nil)

			resp, err := s.web.engine.Test(req)
			So(err, ShouldBeNil)

			body, err := io.ReadAll(resp.Body)
			defer resp.Body.Close()
			So(err, ShouldBeNil)

			s.T().Logf("BODY: %s", body)
		})
	})
}

func (s *WebTestSuite) Test_Experts() {
	Convey("Experts", s.T(), func() {
		Convey("GET", func() {
			t := table.Expert
			db.Truncate(s.web.db, t.TableName())

			var m model.Expert
			So(faker.FakeData(&m), ShouldBeNil)
			m.Since = 0
			_, err := t.INSERT().MODEL(m).Exec(s.web.db)
			So(err, ShouldBeNil)

			req := httptest.NewRequest("GET", "http://localhost/api/experts", nil)

			resp, err := s.web.engine.Test(req)
			So(err, ShouldBeNil)

			body, err := io.ReadAll(resp.Body)
			defer resp.Body.Close()
			So(err, ShouldBeNil)

			s.T().Logf("BODY: %s", body)
		})

		Convey("PATCH", func() {
			t := table.Expert
			db.Truncate(s.web.db, t.TableName())

			var m model.Expert
			So(faker.FakeData(&m), ShouldBeNil)
			m.ID = 1
			m.UID = "110"
			m.Since = 0
			_, err := t.INSERT().MODEL(m).Exec(s.web.db)
			So(err, ShouldBeNil)

			req := httptest.NewRequest("PATCH", "http://localhost/api/experts/110/date", bytes.NewReader([]byte(`{"since": 1}`)))
			req.Header.Set("Content-Type", "application/json")

			_, err = s.web.engine.Test(req)
			So(err, ShouldBeNil)

			// body, err := io.ReadAll(resp.Body)
			// defer resp.Body.Close()
			// So(err, ShouldBeNil)

			// s.T().Logf("BODY: %s", body)

			var u model.Expert
			err = t.SELECT(t.AllColumns).WHERE(t.ID.EQ(Int32(1))).Query(s.web.db, &u)
			So(err, ShouldBeNil)
			So(u.Since, ShouldEqual, 1)
		})

		Convey("POST", func() {
			t := table.Expert
			db.Truncate(s.web.db, t.TableName())

			var m model.Expert
			So(faker.FakeData(&m), ShouldBeNil)
			m.UID = "110"

			b, _ := json.Marshal(m)
			req := httptest.NewRequest("POST", "http://localhost/api/experts", bytes.NewReader(b))
			req.Header.Set("Content-Type", "application/json")

			_, err := s.web.engine.Test(req)
			So(err, ShouldBeNil)

			var u model.Expert
			err = t.SELECT(t.AllColumns).ORDER_BY(t.ID.DESC()).LIMIT(1).Query(s.web.db, &u)
			So(err, ShouldBeNil)
			So(u.UID, ShouldEqual, "110")
		})
	})
}

func (s *WebTestSuite) Test_Follower() {
	logrus.SetLevel(logrus.DebugLevel)
	FocusConvey("Follower", s.T(), func() {
		Convey("GET", func() {
			tu := table.Expert
			t := table.Follower
			db.Truncate(s.web.db, tu.TableName())
			db.Truncate(s.web.db, t.TableName())

			s.web.pendingItems = make(map[int32]time.Time)

			var m model.Expert
			So(faker.FakeData(&m), ShouldBeNil)
			m.UID = "110"
			m.Since = 0
			_, err := tu.INSERT().MODEL(m).Exec(s.web.db)
			So(err, ShouldBeNil)

			var f model.Follower
			So(faker.FakeData(&f), ShouldBeNil)
			f.ExpertUID = "110"
			f.UID = "10"
			f.Followed = 0
			_, err = t.INSERT().MODEL(f).Exec(s.web.db)
			So(err, ShouldBeNil)

			req := httptest.NewRequest("GET", "http://localhost/api/experts/110/follower", nil)

			resp, err := s.web.engine.Test(req)
			So(err, ShouldBeNil)

			body, err := io.ReadAll(resp.Body)
			defer resp.Body.Close()
			So(err, ShouldBeNil)

			s.T().Logf("BODY: %s", body)
		})

		FocusConvey("POST", func() {
			tu := table.Expert
			t := table.Follower
			db.Truncate(s.web.db, tu.TableName())
			db.Truncate(s.web.db, t.TableName())

			s.web.pendingItems = make(map[int32]time.Time)

			var m model.Expert
			So(faker.FakeData(&m), ShouldBeNil)
			m.UID = "110"
			m.Since = 0
			_, err := tu.INSERT().MODEL(m).Exec(s.web.db)
			So(err, ShouldBeNil)

			fs := []model.Follower{}
			var f model.Follower
			for i := 0; i < 5; i++ {
				So(faker.FakeData(&f), ShouldBeNil)
				f.ExpertUID = "110"
				f.UID = fmt.Sprintf("%d", 10+i)
				f.Followed = 0

				fs = append(fs, f)
				fs = append(fs, f)
			}

			b, _ := json.Marshal(fs)
			req := httptest.NewRequest("POST", "http://localhost/api/followers", bytes.NewReader(b))
			req.Header.Set("Content-Type", "application/json")

			_, err = s.web.engine.Test(req)
			So(err, ShouldBeNil)

			var result struct {
				Count int32
			}
			stmt := t.SELECT(COUNT(t.ID).AS("Count"))
			s.T().Log(stmt.DebugSql())
			err = stmt.Query(s.web.db, &result)
			s.T().Logf("%+v", result)
			So(err, ShouldBeNil)
			So(result.Count, ShouldEqual, 5)
		})
	})
}
