package bootable

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"testing"

	"git.simanc.com/software/golib/v4/features/mo"
	"git.simanc.com/software/golib/v4/gnet"
	"git.simanc.com/software/golib/v4/infra/ii"
	"git.simanc.com/software/golib/v4/infra/ii/svc"
	"git.simanc.com/software/golib/v4/log"
)

func TestQueryLimit(t *testing.T) {
	const _testXML = `<?xml version="1.0" encoding="UTF-8"?>
<ItemInfo Name="test.search" Label="bootable">
    <Fields>
        <Field Name="name" Type="string">
            <Label>name</Label>
            <Default/>
        </Field>
        <Field Name="age" Type="long">
            <Label>age</Label>
            <Default/>
        </Field>
    </Fields>
</ItemInfo>`
	filterStr := `{"search":"","sort":"id","order":"desc","offset":3,"limit":10,"filter":"{\"name\":\"simanc\",\"age\":20}"}`
	itemInfo, err := ii.ReadFrom([]byte(_testXML))
	if err != nil {
		t.Error(err)
		return
	}
	filter, err := ResolveFilterFrom([]byte(filterStr))
	if err != nil {
		t.Error(err)
		return
	}
	pipe, err := filter.Build(itemInfo, nil)
	if err != nil {
		t.Error(err)
		return
	}
	for _, d := range pipe {
		b, err := mo.MarshalExtJSON(d, false, true)
		if err != nil {
			t.Error(err)
			return
		}
		t.Log(string(b))
	}
}

func TestInsertTestData(t *testing.T) {
	client, err := mo.Dial("mongodb://root:abcd1234@192.168.0.224:27017/?authSource=admin&readPreference=primary&appname=goland&directConnection=true&ssl=false")
	if err != nil {
		panic(err)
	}
	fi, err := os.ReadFile("_test/name.txt")
	if err != nil {
		t.Error(err)
		return
	}
	names := strings.Split(strings.ReplaceAll(string(fi), "\r", ""), "\n")
	db := client.Database(mo.DefaultDbName)
	bd1 := mo.NewDateTime()

	bootData := make(mo.A, len(names))
	for i, name := range names {
		bootData[i] = mo.M{
			"creationTime": bd1,
			"name":         name,
			"content":      gnet.Rand.Block(10),
			"number":       gnet.Rand.Int64N(9999999),
			"float":        float64(gnet.Rand.Int64N(10000)) / 3.0,
			"array":        mo.A{"a1", "a2", "a3"},
			"oid":          mo.ID.New(),
			"object":       mo.M{"o1": "111", "o2": gnet.Rand.Int64N(10)},
			"objectArray":  mo.A{mo.M{"acc": gnet.Rand.Block(10), "bee": gnet.Rand.Int64N(10)}},
		}
	}

	_ = db.Collection("bootable").Drop(context.Background())
	_, err = db.Collection("bootable").InsertMany(context.Background(), bootData)
	if err != nil {
		t.Error(err)
		return
	}
	bd2 := mo.NewDateTime()
	bootData2 := make(mo.A, len(names))
	for i, name := range names {
		gender := "Female"
		target := "a1"
		if i%2 == 1 {
			gender = "Male"
			target = "a2"
		}
		bootData2[i] = mo.M{"name": name, "age": gnet.Rand.Int64N(99), "target": target, "gender": gender, "phone": strconv.FormatInt(gnet.Rand.Int64N(10000000000), 10), "creationTime": bd2}
	}
	_ = db.Collection("bootable2").Drop(context.Background())
	_, err = db.Collection("bootable2").InsertMany(context.Background(), bootData2)
	if err != nil {
		t.Error(err)
		return
	}
}

func initDefault() {
	items, err := ii.LoadItems("_test")
	if err != nil {
		panic(err)
	}
	perms, err := ii.LoadPerms("../_test/filter.json")
	if err != nil {
		panic(err)
	}
	client, err := mo.Dial("mongodb://root:abcd1234@192.168.0.224:27017/?authSource=admin&readPreference=primary&appname=goland&directConnection=true&ssl=false")
	if err != nil {
		panic(err)
	}
	svc.InitDefault(client, items, perms, log.Console())
}

type svcTestUser mo.M

func (u svcTestUser) Name() string                    { return "" }
func (u svcTestUser) UserName() string                { return "" }
func (u svcTestUser) Flag() bool                      { return true }
func (u svcTestUser) IsSysadmin() bool                { return true }
func (u svcTestUser) Company() mo.ObjectID            { return mo.NilObjectID }
func (u svcTestUser) CompanyALL() mo.A                { return mo.A{} }
func (u svcTestUser) Group(_ string) bool             { return false }
func (u svcTestUser) Role(_ string) (string, bool)    { return "", false }
func (u svcTestUser) Perms(_ string) ([]string, bool) { return []string{}, false }

func (u svcTestUser) ID() mo.ObjectID {
	return mo.ID.New()
}
func (u svcTestUser) Get(k string) any {
	v, ok := u[k]
	if !ok {
		return nil
	}
	return v
}

var (
	testUser ii.User = &svcTestUser{}
)

func initUser() {
	b, err := os.ReadFile("../_test/user.json")
	var info mo.M
	if err = json.Unmarshal(b, &info); err != nil {
		panic(err)
	}
}

func startServer() {
	const (
		testName   = "test.bootable"
		testLookup = "test.bootable2"
	)

	mux := http.NewServeMux()
	mux.HandleFunc("/bootable/example", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			http.Error(w, "only POST method allowed", http.StatusMethodNotAllowed)
			return
		}
		defer func() {
			_ = r.Body.Close()
		}()
		filter, err := ResolveFilter(r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		fstr, _ := json.Marshal(filter)
		fmt.Println("filter:", string(fstr))
		resp, err := Find(testUser, testName, filter)
		if err != nil {
			fmt.Println("Find:", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		fmt.Println(resp.Rows)
		rb, err := json.Marshal(resp)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write(rb)
	})
	addr := "localhost:8181"
	fmt.Println("http.ListenAndServe:", addr)
	err := http.ListenAndServe(addr, mux)
	if err != nil {
		panic(err)
	}
}

func TestListenBootServer(t *testing.T) {
	initDefault()
	initUser()
	startServer()
}
