package framework

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)

// HTTPClient
type HTTPClient struct {
	client    *http.Client
	authToken string
	headers   map[string]string
}

// NewHTTPClient
func NewHTTPClient(authToken string, timeout time.Duration) *HTTPClient {
	return &HTTPClient{
		client: &http.Client{
			Timeout: timeout,
		},
		authToken: authToken,
		headers:   make(map[string]string),
	}
}

// SetHeader set a custom request header
func (c *HTTPClient) SetHeader(key, value string) {
	c.headers[key] = value
}

// SetHeaders set multiple request headers at once
func (c *HTTPClient) SetHeaders(headers map[string]string) {
	for k, v := range headers {
		c.headers[k] = v
	}
}

// Response
type Response struct {
	StatusCode     int
	Body           []byte
	Headers        http.Header
	RequestHeaders map[string]string
	Raw            *http.Response
}

func (r *Response) JSON(v interface{}) error {
	return json.Unmarshal(r.Body, v)
}

func (r *Response) String() string {
	return string(r.Body)
}

func (r *Response) IsSuccess() bool {
	return r.StatusCode >= 200 && r.StatusCode < 300
}

// GetResponseHeaders converts http.Header to map[string]string for reporting
func (r *Response) GetResponseHeaders() map[string]string {
	headers := make(map[string]string)
	for k, v := range r.Headers {
		if len(v) > 0 {
			headers[k] = v[0]
		}
	}
	return headers
}

func (c *HTTPClient) doRequest(method, url string, body interface{}) (*Response, error) {
	var bodyReader io.Reader

	if body != nil {
		jsonBody, err := json.Marshal(body)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal request body: %w", err)
		}
		bodyReader = bytes.NewBuffer(jsonBody)
	}

	req, err := http.NewRequest(method, url, bodyReader)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")

	if c.authToken != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.authToken))
	}

	for k, v := range c.headers {
		req.Header.Set(k, v)
	}

	// Record request headers
	reqHeaders := make(map[string]string)
	for k, v := range req.Header {
		if len(v) > 0 {
			reqHeaders[k] = v[0]
		}
	}

	resp, err := c.client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}

	return &Response{
		StatusCode:     resp.StatusCode,
		Body:           respBody,
		Headers:        resp.Header,
		RequestHeaders: reqHeaders,
		Raw:            resp,
	}, nil
}

func (c *HTTPClient) Get(url string) (*Response, error) {
	return c.doRequest(http.MethodGet, url, nil)
}

func (c *HTTPClient) Post(url string, body interface{}) (*Response, error) {
	return c.doRequest(http.MethodPost, url, body)
}

func (c *HTTPClient) Put(url string, body interface{}) (*Response, error) {
	return c.doRequest(http.MethodPut, url, body)
}

func (c *HTTPClient) Patch(url string, body interface{}) (*Response, error) {
	return c.doRequest(http.MethodPatch, url, body)
}

func (c *HTTPClient) Delete(url string) (*Response, error) {
	return c.doRequest(http.MethodDelete, url, nil)
}

func (c *HTTPClient) DeleteWithBody(url string, body interface{}) (*Response, error) {
	return c.doRequest(http.MethodDelete, url, body)
}
