package fakes

import (
	"context"
	"errors"
	"time"

	SOProto "github.com/AlchemyTelcoSolutions/proto/gen/go/callisto/so/v1"
	commonConverter "github.com/AlchemyTelcoSolutions/utils/service/converter"
	"google.golang.org/grpc"
)

type CallistoFakeSO struct{}

const (
	RequestEmpty             = `request can not be empty"`
	RequestOrdersEmpty       = `request orders can not be empty`
	RequestOrderLinesEmpty   = `order lines can not be empty`
	RequestInvalidCompanyId  = "invalid company id"
	RequestInvalidDate       = "invalid date"
	RequestInvalidOrderRef   = `invalid order reference`
	RequestInvalidShipmentID = `invalid shipment id`
)

func NewCallistoSoFakeClient() *CallistoFakeSO {
	return &CallistoFakeSO{}
}

func (cso *CallistoFakeSO) CreateSaleOrder(_ context.Context, req *SOProto.CreateSaleOrderRequest, _ ...grpc.CallOption) (*SOProto.CreateSaleOrderResponse, error) {
	if req == nil {
		err := errors.New(RequestEmpty)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	}

	if req.Orders == nil {
		err := errors.New(RequestOrdersEmpty)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	}

	for _, order := range req.Orders {
		if order.OrderLines == nil {
			err := errors.New(RequestOrderLinesEmpty)
			return nil, commonConverter.CreateGRPCErrorResponse(err)
		}
	}
	return &SOProto.CreateSaleOrderResponse{
		Orders: []*SOProto.CreateSaleOrderResponse_SaleOrdersResponse{
			{
				OrderRef: "xyzxyz",
			},
		},
	}, nil
}

func (cso *CallistoFakeSO) AddDocument(_ context.Context, req *SOProto.AddDocumentRequest, _ ...grpc.CallOption) (*SOProto.AddDocumentResponse, error) {
	if req == nil {
		err := errors.New(RequestEmpty)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	}

	return &SOProto.AddDocumentResponse{
		Success: true,
	}, nil
}

func (cso *CallistoFakeSO) AddShipment(_ context.Context, req *SOProto.AddShipmentRequest, _ ...grpc.CallOption) (*SOProto.AddShipmentResponse, error) {
	if req == nil {
		err := errors.New(RequestEmpty)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	}

	if req.Shipment == nil {
		err := errors.New(RequestEmpty)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	}

	return &SOProto.AddShipmentResponse{
		ShipmentId: 123456,
	}, nil
}

func (cso *CallistoFakeSO) AddNote(_ context.Context, req *SOProto.AddNoteRequest, _ ...grpc.CallOption) (*SOProto.AddNoteResponse, error) {
	if req == nil {
		err := errors.New(RequestEmpty)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	}

	return &SOProto.AddNoteResponse{
		Success: true,
	}, nil
}

func (cso *CallistoFakeSO) UpdateStatus(_ context.Context, re *SOProto.UpdateStatusRequest, _ ...grpc.CallOption) (*SOProto.UpdateStatusResponse, error) {
	//implement fake logic in here
	return nil, nil
}

func (cso *CallistoFakeSO) AddAsn(_ context.Context, re *SOProto.AddAsnRequest, _ ...grpc.CallOption) (*SOProto.AddAsnResponse, error) {
	//implement fake logic in here
	return nil, nil
}

func (cso *CallistoFakeSO) AddApproval(_ context.Context, re *SOProto.AddApprovalRequest, _ ...grpc.CallOption) (*SOProto.AddApprovalResponse, error) {
	//implement fake logic in here
	return nil, nil
}

func (cso *CallistoFakeSO) UpdateNote(_ context.Context, re *SOProto.UpdateNoteRequest, _ ...grpc.CallOption) (*SOProto.UpdateNoteResponse, error) {
	//implement fake logic in here
	return nil, nil
}

func (cso *CallistoFakeSO) UpdateShipment(_ context.Context, re *SOProto.UpdateShipmentRequest, _ ...grpc.CallOption) (*SOProto.UpdateShipmentResponse, error) {
	//implement fake logic in here
	return nil, nil
}

func (cso *CallistoFakeSO) RemoveDocument(_ context.Context, re *SOProto.RemoveDocumentRequest, _ ...grpc.CallOption) (*SOProto.RemoveDocumentResponse, error) {
	//implement fake logic in here
	return nil, nil
}

// GetSummary fakes callisto-so GetSummary rpc
func (cso *CallistoFakeSO) GetSummary(ctx context.Context, in *SOProto.GetSummaryRequest, opts ...grpc.CallOption) (*SOProto.GetSummaryResponse, error) {
	if in == nil {
		err := errors.New(RequestEmpty)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	}
	if in.BuyerCompanyId <= 0 {
		err := errors.New(RequestInvalidCompanyId)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	}
	if in.CancelledSince != nil {
		since := in.CancelledSince.AsTime()
		if since.After(time.Now()) {
			err := errors.New(RequestInvalidDate)
			return nil, commonConverter.CreateGRPCErrorResponse(err)
		}
	}
	return &SOProto.GetSummaryResponse{
		Details: []*SOProto.GetSummaryResponse_Detail{
			{
				StatusId: uint64(SOProto.Status_STATUS_APPROVAL),
				Count:    1,
			},
		},
		Groups: &SOProto.GetSummaryResponse_Group{
			RequiresAction: 1,
			Processing:     0,
			Complete:       0,
			Cancelled:      0,
		},
	}, nil
}
func (cso *CallistoFakeSO) GetNewReference(_ context.Context, re *SOProto.GetNewReferenceRequest, _ ...grpc.CallOption) (*SOProto.GetNewReferenceResponse, error) {
	//implement fake logic in here
	return nil, nil
}

func (cso *CallistoFakeSO) GetInvoices(_ context.Context, re *SOProto.GetInvoicesRequest, _ ...grpc.CallOption) (*SOProto.GetInvoicesResponse, error) {
	return &SOProto.GetInvoicesResponse{
		Invoices: []*SOProto.Invoice{
			{
				Id: 123456,
			},
		},
	}, nil
}

// GetShipments implements sale_order.SOServiceClient.
func (cso *CallistoFakeSO) GetShipments(ctx context.Context, req *SOProto.GetShipmentsRequest, opts ...grpc.CallOption) (*SOProto.GetShipmentsResponse, error) {

	if req.OrderRef == "" {
		err := errors.New(RequestInvalidOrderRef)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	}

	switch req.OrderRef {
	case "0": // to mock response with failure to get shipments data
		err := errors.New(RequestInvalidOrderRef)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	case "1234": // to mock response with no ASN data
		return &SOProto.GetShipmentsResponse{
			Shipments: []*SOProto.Shipment{},
		}, nil
	case "12340": // to mock response with no ASN data
		return &SOProto.GetShipmentsResponse{
			Shipments: []*SOProto.Shipment{
				{
					ShipmentId:   0,
					OrderRef:     req.OrderRef,
					IsCollection: false,
				},
			},
		}, nil
	case "12341": // to mock response with no ASN data
		return &SOProto.GetShipmentsResponse{
			Shipments: []*SOProto.Shipment{
				{
					ShipmentId:   1,
					OrderRef:     req.OrderRef,
					IsCollection: false,
				},
			},
		}, nil
	default:
		return &SOProto.GetShipmentsResponse{
			Shipments: []*SOProto.Shipment{
				{
					ShipmentId:   2,
					OrderRef:     req.OrderRef,
					IsCollection: false,
				},
			},
		}, nil
	}
}

func (cso *CallistoFakeSO) GetAsnData(ctx context.Context, req *SOProto.GetAsnDataRequest, opts ...grpc.CallOption) (*SOProto.GetAsnDataResponse, error) {
	if req.ShipmentId == 0 {
		err := errors.New(RequestInvalidShipmentID)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	}

	switch req.ShipmentId {
	case 0: // to mock response with failure to get ASN data
		err := errors.New(RequestInvalidShipmentID)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	case 1: // to mock response with no ASN data
		return &SOProto.GetAsnDataResponse{
			OrderRef:   "1234",
			ShipmentId: req.ShipmentId,
			AsnItems:   []*SOProto.AsnItem{},
		}, nil
	default:
		return &SOProto.GetAsnDataResponse{
			OrderRef:   "1234",
			ShipmentId: req.ShipmentId,
			AsnItems: []*SOProto.AsnItem{
				{
					Sku:              "SKU-1234",
					Serial:           "SERIAL-1234",
					Imei:             "IMEI-1234",
					NonSerial:        false,
					ProductName:      "PRODUCT-1234",
					ShippingCartonId: "",
				},
			},
		}, nil
	}
}

// CheckOrderAffiliation implements sale_order.SOServiceClient.
func (*CallistoFakeSO) CheckOrderAffiliation(ctx context.Context, req *SOProto.CheckOrderAffiliationRequest, opts ...grpc.CallOption) (*SOProto.CheckOrderAffiliationResponse, error) {
	//implement fake logic in here
	if req.Reference == "" {
		err := errors.New(RequestEmpty)
		return nil, commonConverter.CreateGRPCErrorResponse(err)
	}

	resp := &SOProto.CheckOrderAffiliationResponse{
		BuyerAffiliated:  true,
		SellerAffiliated: true,
	}

	if req.BuyerCompanyId > 0 {
		resp.BuyerAffiliated = true
	}

	if req.SellerCompanyId > 0 {
		resp.SellerAffiliated = true
	}

	return resp, nil
}
