package main

import (
	"context"
	"flag"
	"fmt"
	"math/rand/v2"
	"os/signal"
	"syscall"
	"time"

	"github.com/typesense/typesense-go/v2/typesense"
	"github.com/typesense/typesense-go/v2/typesense/api"
	"github.com/typesense/typesense-go/v2/typesense/api/pointer"
)

func main() {
	if err := run(); err != nil {
		panic(err)
	}
}

func run() error {
	var url, apiKey string
	var list, del bool
	var delay time.Duration
	flag.StringVar(&url, "url", "url", "url")
	flag.StringVar(&apiKey, "api-key", "nope", "api key")
	flag.BoolVar(&list, "list", false, "whether to list 10 latest documents from collection")
	flag.BoolVar(&del, "delete", false, "whether delete collection")
	flag.DurationVar(&delay, "delay", time.Second, "delay between requests")
	flag.Parse()

	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	client := typesense.NewClient(
		typesense.WithServer(url),
		typesense.WithAPIKey(apiKey),
	)

	{
		ok, err := client.Health(ctx, 5*time.Second)
		if err != nil {
			return err
		}
		if !ok {
			return fmt.Errorf("health check failed")
		}
	}

	const collectionName = "companies"

	if del {
		_, err := client.Collection(collectionName).Delete(ctx)
		if err != nil {
			return err
		}
		fmt.Printf("deleted collection: %s\n", collectionName)
		time.Sleep(3 * time.Second)
	}

	var exists bool
	{
		collections, err := client.Collections().Retrieve(ctx)
		if err != nil {
			return err
		}
		for _, collection := range collections {
			fmt.Printf("listed collection: %s\n", collection.Name)
			if collection.Name == collectionName {
				exists = true
			}
		}
	}
	if !exists {
		collection, err := client.Collections().Create(ctx, &api.CollectionSchema{
			Name: "companies",
			Fields: []api.Field{
				{
					Name: "num_employees",
					Type: "int32",
				},
				{
					Name: "created",
					Type: "string",
				},
				{
					Name: "unix",
					Type: "int64",
				},
			},
			DefaultSortingField: pointer.String("unix"),
		})
		if err != nil {
			return err
		}
		fmt.Printf("created collection: %s\n", collection.Name)
		time.Sleep(3 * time.Second)
	}

	if list {
		search, err := client.Collection(collectionName).Documents().Search(ctx, &api.SearchCollectionParams{
			Q:      pointer.String("*"),
			Limit:  pointer.Int(250),
			SortBy: pointer.String("unix:desc"),
		})
		if err != nil {
			return err
		}
		for i := range *search.Hits {
			hit := (*search.Hits)[len(*search.Hits)-1-i]
			doc := *hit.Document
			fmt.Printf("LIST: ==> %s (%s)\n", doc["created"], doc["id"])
		}
	}

	for {
		if ctx.Err() != nil {
			fmt.Println("context canceled")
			return nil
		}

		now := time.Now()
		document := map[string]any{
			"num_employees": rand.Int32N(10000),
			"created":       now.Format(time.TimeOnly),
			"unix":          now.Unix(),
		}

		var id string
		documentResponse, err := client.Collection(collectionName).Documents().Create(ctx, document)
		if err != nil {
			fmt.Printf("create document failed: %v\n", err)
			continue
		}
		fmt.Printf("%s ==>\n", document["created"])

		id = documentResponse["id"].(string)
		for {
			documentResponse, err = client.Collection(collectionName).Document(id).Retrieve(ctx)
			if err != nil {
				if ctx.Err() != nil {
					fmt.Println("context canceled")
					return nil
				}
				time.Sleep(100 * time.Millisecond)
				continue
			}
			fmt.Printf("==> %s (%s)\n", documentResponse["created"], documentResponse["id"])
			break
		}

		time.Sleep(delay)
	}
}
