//go:build e2e

package client

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

// Client is a thin HTTP client wrapper for E2E API scenarios.
type Client struct {
	baseURL    string
	httpClient *http.Client
	token      string
	workspace  string
}

func New(baseURL string) *Client {
	return &Client{
		baseURL: strings.TrimRight(baseURL, "/"),
		// Let per-request contexts from testkit control scenario deadlines.
		// A fixed client timeout shorter than PeerFlowTimeout/APITimeout can
		// turn embedded-server load spikes into spurious Client.Timeout flakes.
		httpClient: &http.Client{},
	}
}

func (c *Client) WithToken(token string) *Client {
	cp := *c
	cp.token = token
	return &cp
}

func (c *Client) WithWorkspace(workspaceID string) *Client {
	cp := *c
	cp.workspace = workspaceID
	return &cp
}

func (c *Client) DoJSON(ctx context.Context, method, path string, reqBody any, out any) (int, []byte, error) {
	var body io.Reader
	if reqBody != nil {
		raw, err := json.Marshal(reqBody)
		if err != nil {
			return 0, nil, fmt.Errorf("marshal request: %w", err)
		}
		body = bytes.NewReader(raw)
	}

	req, err := http.NewRequestWithContext(ctx, method, c.baseURL+path, body)
	if err != nil {
		return 0, nil, fmt.Errorf("build request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	if c.token != "" {
		req.Header.Set("Authorization", "Bearer "+c.token)
	}
	if c.workspace != "" {
		req.Header.Set("X-Workspace-Id", c.workspace)
	}

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

	raw, err := io.ReadAll(resp.Body)
	if err != nil {
		return resp.StatusCode, nil, fmt.Errorf("read response: %w", err)
	}
	if out != nil && len(raw) > 0 {
		if err := json.Unmarshal(raw, out); err != nil {
			return resp.StatusCode, raw, fmt.Errorf("decode response: %w", err)
		}
	}

	return resp.StatusCode, raw, nil
}
