package internal

import (
	"context"
	"fmt"
	"math"

	api "git.vdhsn.com/barretthousen/barretthousen/src/catalog/api/grpc"
	"git.vdhsn.com/barretthousen/barretthousen/src/catalog/internal/domain"
	"git.vdhsn.com/barretthousen/barretthousen/src/lib/kernel"
	"google.golang.org/grpc"
	"google.golang.org/protobuf/types/known/timestamppb"
)

type Domain interface {
	GetUpcoming(context.Context, domain.UpcomingQuery) (domain.UpcomingResults, error)
	ImportAuction(context.Context, domain.ImportAuctionMessage) (domain.AuctionCreated, error)
}

type catalogHandler struct {
	api.UnimplementedCatalogServer
	domain Domain
}

func NewCatalogServer(d *domain.Usecase) func(grpcServer grpc.ServiceRegistrar, endpoint string) {
	return func(grpcServer grpc.ServiceRegistrar, endpoint string) {
		api.RegisterCatalogServer(grpcServer, &catalogHandler{domain: d})
	}
}

func (rh *catalogHandler) GetUpcoming(ctx context.Context, cmd *api.AuctionSearchCriteria) (out *api.GetUpcomingResult, err error) {
	page := int(math.Max(0, float64(cmd.GetPage())))
	pageSize := int(math.Max(32, float64(cmd.GetLimit())))

	if pageSize < 32 {
		pageSize = 32
	} else if pageSize > 128 {
		pageSize = 128
	}

	kernel.TraceLog.Printf("GetUpcoming[searchTerm: %q - page: %d - pageSize: %d]", cmd.GetSearchTerm(), page, pageSize)
	var queryResp domain.UpcomingResults
	if queryResp, err = rh.domain.GetUpcoming(ctx, domain.UpcomingQuery{
		Term:  cmd.SearchTerm,
		Page:  page,
		Limit: pageSize,
	}); err != nil {
		err = fmt.Errorf("could not get upcoming items from domain: %w", err)
		return
	}

	out = &api.GetUpcomingResult{
		Page:    int32(page),
		Total:   int32(queryResp.Total),
		Found:   int32(queryResp.Found),
		Results: make([]*api.Auction, len(queryResp.Results)),
	}

	for idx, a := range queryResp.Results {
		out.Results[idx] = &api.Auction{
			Id:             int32(a.ID),
			Fingerprint:    a.Fingerprint,
			Items:          int32(a.ItemCount),
			Start:          timestamppb.New(a.Start),
			End:            timestamppb.New(a.End),
			Title:          a.Title,
			Description:    a.Description,
			SourceSiteURL:  a.SourceSiteURL,
			SourceSiteName: a.SourceSiteName,
			SourceURL:      a.SourceURL,
			Country:        a.Country,
			Province:       a.Province,
		}
	}
	return
}

func (rh *catalogHandler) ImportAuction(ctx context.Context, cmd *api.ImportAuctionMessage) (r *api.AuctionCreated, err error) {
	var evt domain.AuctionCreated
	if evt, err = rh.domain.ImportAuction(ctx, domain.ImportAuctionMessage{
		Auction: domain.Auction{
			Title:          cmd.Title,
			Description:    cmd.Description,
			SourceSiteURL:  cmd.SourceSiteURL,
			SourceSiteName: cmd.SourceSiteName,
			SourceURL:      cmd.SourceURL,
			Country:        cmd.Country,
			Province:       cmd.Province,
			ItemCount:      int(cmd.Items),
			Start:          cmd.Start.AsTime(),
			End:            cmd.End.AsTime(),
		},
	}); err != nil {
		err = fmt.Errorf("could not import auction into domain: %w", err)
	}
	r = &api.AuctionCreated{
		Duplicate: evt.Duplicate,
		Auction: &api.Auction{
			Id:             int32(evt.Auction.ID),
			Items:          int32(evt.Auction.ItemCount),
			Fingerprint:    evt.Fingerprint,
			Start:          timestamppb.New(evt.Auction.Start),
			End:            timestamppb.New(evt.Auction.End),
			Title:          evt.Auction.Title,
			Description:    evt.Auction.Description,
			SourceSiteURL:  evt.Auction.SourceSiteURL,
			SourceSiteName: evt.Auction.SourceSiteName,
			SourceURL:      evt.Auction.SourceURL,
			Country:        evt.Auction.Country,
			Province:       evt.Auction.Province,
		},
	}
	return
}
