package products

import (
	"context"
	"fmt"
	"github.com/samber/lo"
	"google.golang.org/grpc"
	pb "sipro-mps/api/generated/v1/ozon/products"
	"sipro-mps/internal/marketplace"
)

type AdapterGRPC struct {
	pb.UnimplementedProductsServiceServer
	repo Repository
}

func NewAdapterGRPC(repo Repository) *AdapterGRPC {
	return &AdapterGRPC{
		repo: repo,
	}
}

// RegisterAdapterGRPC registers the gRPC server for the Products service.
func RegisterAdapterGRPC(server *grpc.Server, marketplaceRepo marketplace.Repository) (repo *Repository, err error) {
	apiRepo := NewAPIRepository(marketplaceRepo)
	adapter := NewAdapterGRPC(apiRepo)
	pb.RegisterProductsServiceServer(server, adapter)
	return &apiRepo, nil
}
func (g *AdapterGRPC) GetListOfProducts(req *pb.GetListOfProductsRequest, stream pb.ProductsService_GetListOfProductsServer) error {
	ctx := stream.Context()
	fmt.Printf("GetListOfProducts called with req: %+v\n", req.MarketplaceId)
	resultChan := make(chan []pb.Product, 10)
	errChan := make(chan error)

	go g.repo.StreamAllProductsCache(ctx, int(req.MarketplaceId), resultChan, errChan) // Запускаем в горутине
	for {
		select {
		case <-ctx.Done():
			fmt.Println("GetListOfProducts: context cancelled or deadline exceeded:", ctx.Err())
			return ctx.Err()
		case products, ok := <-resultChan:
			if !ok {
				return nil // Завершаем, только если результат закрыт
			}
			resp := &pb.GetListOfProductsResponse{
				Products: lo.Map(products, func(p pb.Product, _ int) *pb.Product {
					return &p
				}),
			}
			if err := stream.Send(resp); err != nil {
				fmt.Println("GetListOfProducts: error sending response:", err)
				return err
			}
		case err, ok := <-errChan:
			if ok && err != nil {
				fmt.Println("GetListOfProducts: error received from channel:", err)
				return err
			}
		}
	}
}

func (g *AdapterGRPC) GetProductAttributes(req *pb.GetProductAttributesRequest, stream pb.ProductsService_GetProductAttributesServer) error {
	ctx := stream.Context()

	resultChan := make(chan []pb.ProductAttributes, 10)
	errChan := make(chan error)
	productIds := lo.Map(req.ProductId, func(item int64, index int) int {
		return int(item)
	})
	go g.repo.StreamProductAttributesCache(ctx, int(req.MarketplaceId), productIds, resultChan, errChan)

	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case attrs, ok := <-resultChan:
			if !ok {
				return nil
			}
			items := lo.Map(attrs, func(item pb.ProductAttributes, index int) *pb.ProductAttributes {
				return &item
			})
			resp := pb.GetProductAttributesResponse{Items: items}
			if err := stream.Send(&resp); err != nil {
				return err
			}
		case err, ok := <-errChan:
			if !ok {
				return nil
			}
			return err
		}
	}
}

func (g *AdapterGRPC) DeleteProducts(ctx context.Context, req *pb.DeleteProductsRequest) (*pb.DeleteProductsResponse, error) {
	responseItems, err := g.repo.DeleteProducts(ctx, int(req.MarketplaceId), req.Items)
	if err != nil {
		return nil, err
	}
	return &pb.DeleteProductsResponse{
			MarketplaceId: req.MarketplaceId,
			Items:         responseItems,
		},
		nil
}

func (g *AdapterGRPC) CreateOrUpdateProducts(ctx context.Context, req *pb.CreateOrUpdateProductsRequest) (*pb.CreateOrUpdateProductsResponse, error) {
	return g.repo.CreateOrUpdateProducts(ctx, int(req.MarketplaceId), req.Items)
}
