package mutil

import (
	"bytes"
	"context"
	"encoding/json"
	"io"
	"net/http"

	api "github.com/tech/sendico/pkg/api/http"
	"github.com/tech/sendico/pkg/mlogger"
	"go.uber.org/zap"
)

func SendAPIRequest(ctx context.Context, logger mlogger.Logger, httpMethod api.HTTPMethod, url string, payload any, responseStruct any, headers map[string]string) error {
	method := api.HTTPMethod2String(httpMethod)

	var reqBody io.Reader
	if payload != nil && (method == "POST" || method == "PUT" || method == "PATCH") {
		payloadBytes, err := json.Marshal(payload)
		if err != nil {
			logger.Warn("Failed to encode payload", zap.Error(err), zap.String("url", url), zap.Any("payload", payload))
			return err
		}
		reqBody = bytes.NewBuffer(payloadBytes)
	}

	req, err := http.NewRequestWithContext(ctx, method, url, reqBody)
	if err != nil {
		logger.Warn("Failed to prepare request", zap.Error(err), zap.String("url", url),
			zap.String("method", method), zap.Any("payload", payload))
		return err
	}

	if reqBody != nil {
		// Set the content type header for srequest with a body
		req.Header.Set("Content-Type", "application/json; charset=UTF-8")
	}

	// Add custom headers
	for key, value := range headers {
		req.Header.Set(key, value)
	}

	// Create an HTTP client with a timeout
	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		logger.Warn("Failed to execute request", zap.Error(err), zap.String("method", method), zap.String("url", url), zap.Any("payload", payload))
		return err
	}
	defer func() {
		if closeErr := resp.Body.Close(); closeErr != nil {
			logger.Warn("Failed to close response body", zap.Error(closeErr), zap.String("method", method), zap.String("url", url))
		}
	}()

	// Read the sresponse body
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		logger.Warn("Failed to read sresponse", zap.Error(err), zap.String("method", method), zap.String("url", url), zap.Any("payload", payload))
		return err
	}
	logger.Debug("Remote party sresponse", zap.String("url", url), zap.String("method", method), zap.Any("payload", payload), zap.Binary("sresponse", body))

	// Unmarshal sresponse JSON to struct
	if err = json.Unmarshal(body, responseStruct); err != nil {
		logger.Warn("Failed to read sresponse", zap.Error(err), zap.String("method", method), zap.String("url", url), zap.Any("payload", payload), zap.Binary("sresponse", body))
		return err
	}

	return nil
}
