package proxy

import (
	"fmt"
	"io"
	"net/http"
	"strings"

	"github.com/AlchemyTelcoSolutions/xutils-go/xlogger"
	"go.uber.org/zap"

	"github.com/AlchemyTelcoSolutions/callisto-so-bff/cmd/app/clients"
	"github.com/AlchemyTelcoSolutions/callisto-so-bff/cmd/app/config"
	"github.com/AlchemyTelcoSolutions/callisto-so-bff/internal/domain/model"
)

// NewProxyService return a nes Sale Order Service
func NewProxyService() *Service {
	return &Service{}
}

// SetLogger is a setter for logger
func (s *Service) SetLogger(l xlogger.Logger) *Service {
	s.logger = l
	return s
}

// SetConfigs is a setter for Configs
func (s *Service) SetConfigs(c config.AppConfig) *Service {
	s.configs = c
	return s
}

// SetHttpClient is a setter for http client
func (s *Service) SetHttpClient(h clients.HTTPProxyClientInterface) *Service {
	s.httpClient = h
	return s
}

func (s *Service) ForwardProxy(r *http.Request, fromPath string) *model.ProxyResponse {
	// get the configs
	config := s.configs.GetConfigurations()

	// get the new path to proxy the call
	path := strings.Join(
		[]string{
			config.CallistoAPI.Host,
			getReplacedPath(fromPath, config.HTTP.ApiPrefix, config),
		}, "",
	)

	s.logger.Info(fmt.Sprintf("proxing from %s, to %s, with method %s", r.URL.Path, path, r.Method))
	resp, err := s.SendRequestToClient(r, path, map[string]string{})
	if err != nil {
		s.logger.Error("error proxing requiest to callisto-api: ", err)
		return &model.ProxyResponse{
			StatusCode: http.StatusInternalServerError,
		}
	}
	s.logger.Info(
		fmt.Sprintf("response from %s", path),
		zap.Int("status", resp.StatusCode),
		zap.String("body", string(resp.BodyBytes)),
	)
	return resp
}

// SendRequestToClient makes a requets to API
func (s *Service) SendRequestToClient(r *http.Request, path string, extraHeaders map[string]string) (*model.ProxyResponse, error) {
	proxyRes, err := s.httpClient.DoProxy(r, path, extraHeaders)
	if err != nil {
		return nil, fmt.Errorf("error sending request to callisto api,  %w", err)
	}
	defer proxyRes.Body.Close()
	bodyBytes, err := io.ReadAll(proxyRes.Body)
	if err != nil {
		return nil, fmt.Errorf("error reading body from response: %w", err)
	}
	return &model.ProxyResponse{
		StatusCode: proxyRes.StatusCode,
		BodyBytes:  bodyBytes,
	}, nil
}

func getReplacedPath(fromPath, prefix string, c *config.Config) string {
	if len(c.HTTP.Proxy.Paths) < 1 {
		return strings.TrimPrefix(fromPath, prefix)
	}
	for from, to := range c.HTTP.Proxy.Paths {
		if strings.HasPrefix(fromPath, from) {
			return strings.ReplaceAll(fromPath, from, to)
		}
	}
	return fromPath
}
