package liveauctioneers

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"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"
)

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

type LAAuctionFinder string

func (la LAAuctionFinder) String() string {
	return string(la)
}

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

	fetched := -1
	total := 0
	page := uint(0)

	for fetched < limit {
		var ids LACatalogIDs
		if ids, total, err = LAGetUpcomingSaleIDs(ctx, GetUpcomingSaleIDsInput{
			Page:  page,
			Limit: 128,
		}); err != nil {
			return err
		}

		if limit <= 0 {
			limit = total
		}

		var auctions []catalog.Auction
		if auctions, err = LAGetSaleInfo(ctx, ids); err != nil {
			return err
		}

		fetched += len(auctions)
		for _, a := range auctions {
			results <- a
		}

		if fetched >= limit {
			break
		}
		page++
	}

	return
}

type GetUpcomingSaleIDsInput struct {
	Page  uint
	Limit uint
}

func LAGetUpcomingSaleIDs(ctx context.Context, in GetUpcomingSaleIDsInput) (ids LACatalogIDs, total int, err error) {
	if in.Limit == 0 {
		in.Limit = 128
	}

	req, _ := http.NewRequestWithContext(
		ctx,
		http.MethodGet,
		fmt.Sprintf(
			"https://search-party-prod.liveauctioneers.com/search/catalogsearch?page=%d&sort=saleStart&pageSize=%d",
			in.Page,
			in.Limit,
		),

		nil)

	req.Header.Set("User-Agent", "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/112.0")
	req.Header.Set("Accept", "*/*")
	req.Header.Set("Accept-Encoding", "gzip")
	req.Header.Set("Cache-Control", "no-cache")

	var res *http.Response
	if res, err = http.DefaultClient.Do(req); err != nil {
		return
	}

	if res.StatusCode > 299 {
		err = fmt.Errorf("Got a bad http status: %d", res.StatusCode)
		return
	}

	defer res.Body.Close()
	var searchResults struct {
		Payload struct {
			Count   int `json:"count"`
			Results []struct {
				ID int `json:"catid"`
			} `json:"results"`
		} `json:"payload"`
	}

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

	if searchResults.Payload.Results == nil {
		return
	}

	total = searchResults.Payload.Count

	ids = LACatalogIDs(make([]int, len(searchResults.Payload.Results)))
	for idx, result := range searchResults.Payload.Results {
		ids[idx] = result.ID
	}

	return
}

type LACatalogIDs []int

func (cip LACatalogIDs) String() string {
	sb := &strings.Builder{}
	sb.WriteString("{\"ids\":[")
	for idx, catID := range cip {
		fmt.Fprintf(sb, "%d", catID)
		if idx < len(cip)-1 {
			sb.WriteString(",")
		}
	}

	sb.WriteString("]}")

	return sb.String()
}

func LAGetSaleInfo(ctx context.Context, catIDs LACatalogIDs) (results []catalog.Auction, err error) {
	if catIDs == nil || len(catIDs) == 0 {
		return
	}

	req, _ := http.NewRequestWithContext(ctx, http.MethodPost,
		"https://item-api-prod.liveauctioneers.com/spa/small/catalogs",
		strings.NewReader(catIDs.String()))

	req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:97.0) Gecko/20100101 Firefox/97.0")
	req.Header.Set("Content-Type", "application/json")

	var res *http.Response
	if res, err = http.DefaultClient.Do(req); err != nil {
		err = fmt.Errorf("could not complete request: %w", err)
		return
	}

	if res.StatusCode >= 300 {
		err = fmt.Errorf("got non 200 status: %d", res.StatusCode)
		return
	}

	defer res.Body.Close()

	var apiResults struct {
		Data struct {
			Catalogs []struct {
				ID          int    `json:"catalogId"`
				Title       string `json:"title"`
				Description string `json:"description"`
				ItemCount   int    `json:"lotsListed"`
				SaleStartTS int64  `json:"saleStartTs"`
				SellerID    int64  `json:"sellerId"`
				Address     struct {
					CountryCode string  `json:"country"`
					Lat         float64 `json:"lat"`
					Long        float64 `json:"lng"`
					State       string  `json:"state"`
					City        string  `json:"city"`
				} `json:"address"`
			} `json:"catalogs"`
		} `json:"data"`
	}

	if err = json.NewDecoder(res.Body).Decode(&apiResults); err != nil {
		err = fmt.Errorf("could not parse response: %w", err)
		return
	}

	results = make([]catalog.Auction, len(apiResults.Data.Catalogs))
	for idx, c := range apiResults.Data.Catalogs {
		results[idx] = catalog.Auction{
			Title:       c.Title,
			Description: c.Description,
			Start:       time.Unix(c.SaleStartTS, 0),
			End:         time.Unix(c.SaleStartTS, 0).Add(time.Hour * 8),
			ItemCount:   c.ItemCount,
			Source: catalog.SourceDetail{
				SiteURL:    "https://www.liveauctioneers.com",
				Name:       "Live Auctioneers",
				AuctionURL: fmt.Sprintf("https://www.liveauctioneers.com/catalog/%d", c.ID),
			},
			Address: catalog.AddressDetail{
				CountryCode: c.Address.CountryCode,
				City:        c.Address.City,
				State:       c.Address.State,
				Lat:         c.Address.Lat,
				Long:        c.Address.Long,
			},
			Seller: catalog.SellerDetail{
				ID: int(c.SellerID),
			},
		}
	}

	return
}
