package catawiki

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"time"

	catalog "git.vdhsn.com/barretthousen/barretthousen/src/catalog/api"
	"git.vdhsn.com/barretthousen/barretthousen/src/lib/kernel"
	"git.vdhsn.com/barretthousen/barretthousen/src/runner/internal/domain"
	"golang.org/x/sync/errgroup"
)

func init() {
	kernel.TraceLog.Println("Registering AuctionFinder catawiki")
	domain.RegisterAuctionFinder(
		CatawikiAuctionFinder("catawiki"),
	)
}

type CatawikiAuctionFinder string

func (cw CatawikiAuctionFinder) String() string {
	return string(cw)
}

func (cw CatawikiAuctionFinder) Find(ctx context.Context, limit int, results chan<- catalog.Auction) (err error) {
	defer close(results)

	pageSize := 50

	if limit > 1000 {
		limit = 1000
	}

	if limit <= 0 {
		limit = 1000
	}

	errg, errgCtx := errgroup.WithContext(ctx)
	errg.SetLimit(5)

	kernel.TraceLog.Printf("[Catawiki] fetching %d pages or up to %d results in %d batches", limit/pageSize, limit, pageSize)
	for p := 1; p <= limit/pageSize; p++ {
		pageIdx := p
		errg.Go(func() error {
			auctionR, err := GetUpcomingAuctions(errgCtx, limit, pageIdx)
			if errors.Is(err, ErrNoResults) {
				kernel.TraceLog.Println("no more results from catawiki api")
				return err
			} else if err != nil {
				return err
			}

			for _, r := range auctionR {
				select {
				case <-errgCtx.Done():
					kernel.TraceLog.Println("[Catawiki] group context exited early")
					return nil
				default:
					results <- r
					kernel.TraceLog.Printf("[Catawiki]: %+v", r)
				}
			}
			return nil
		})
	}

	if err = errg.Wait(); err != nil {
		kernel.ErrorLog.Printf("[Catawiki] could not get results f: %v", err)
		return
	}

	return
}

var ErrNoResults = errors.New("No results found")

func GetUpcomingAuctions(ctx context.Context, limit int, page int) (results []catalog.Auction, err error) {
	if limit <= 25 {
		limit = 25
	} else if limit >= 50 {
		limit = 50
	}

	if page < 1 {
		page = 1
	}

	url := fmt.Sprintf("https://www.catawiki.com/buyer/api/v1/auctions?locale=en&=published_at_desc&per_page=%d&page=%d", limit, page)

	req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)

	req.Header.Add("Accept", "application/json")
	req.Header.Add("Accept-Language", "en-US,en;q=0.5")
	req.Header.Add("User-Agent", "barretthousen.com Auction Search Engine")

	kernel.TraceLog.Printf("[Catawiki] Loading page %d of %d results", page, limit)
	var res *http.Response
	if res, err = http.DefaultClient.Do(req); err != nil {
		return
	}

	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		err = fmt.Errorf("bad response code: %d", res.StatusCode)
		return
	}

	type Payload struct {
		Auctions []struct {
			ID       int    `json:"id"`
			StartAt  string `json:"start_at"`
			CloseAt  string `json:"close_at"`
			LotCount int    `json:"lot_count"`
			Title    string `json:"title"`
			URL      string `json:"url"`
			Status   string `json:"status"`
			Sellers  []struct {
				ID   int    `json:"id"`
				Name string `json:"name"`
			} `json:"sellers"`
		} `json:"auctions"`
	}

	var p Payload
	if err = json.NewDecoder(res.Body).Decode(&p); err != nil {
		return
	}

	if len(p.Auctions) <= 0 {
		err = ErrNoResults
		return
	}

	results = make([]catalog.Auction, len(p.Auctions))
	for idx, auction := range p.Auctions {
		results[idx] = catalog.Auction{
			Title:          auction.Title,
			Description:    "",
			SourceSiteURL:  "https://www.catawiki.com",
			SourceSiteName: "CataWiki",
			SourceURL:      auction.URL,
			Country:        "",
			Province:       "",
			ItemCount:      auction.LotCount,
		}

		results[idx].Start, _ = time.Parse(time.RFC3339, auction.StartAt)
		results[idx].End, _ = time.Parse(time.RFC3339, auction.CloseAt)
	}

	return
}
