package agentidp

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

// doRequest performs an authenticated JSON HTTP request.
//
//   - method: HTTP method (GET, POST, PATCH, DELETE)
//   - url:    full URL (base + path + query)
//   - body:   request body (marshalled to JSON), or nil for bodyless requests
//   - token:  Bearer token for Authorization header
//   - out:    pointer to unmarshal the response body into, or nil to discard
//
// Returns nil on 2xx; returns *AgentIdPError on HTTP errors or network failures.
// 204 No Content responses are considered success; out is not populated.
func doRequest(ctx context.Context, client *http.Client, method, url string, body interface{}, token string, out interface{}) error {
	var bodyReader io.Reader
	if body != nil {
		b, err := json.Marshal(body)
		if err != nil {
			return &AgentIdPError{
				Code:       "SERIALIZATION_ERROR",
				Message:    fmt.Sprintf("failed to marshal request body: %s", err.Error()),
				HTTPStatus: 0,
			}
		}
		bodyReader = bytes.NewReader(b)
	}

	req, err := http.NewRequestWithContext(ctx, method, url, bodyReader)
	if err != nil {
		return &AgentIdPError{
			Code:       "REQUEST_BUILD_ERROR",
			Message:    fmt.Sprintf("failed to build request: %s", err.Error()),
			HTTPStatus: 0,
		}
	}

	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	req.Header.Set("Accept", "application/json")
	if token != "" {
		req.Header.Set("Authorization", "Bearer "+token)
	}

	resp, err := client.Do(req)
	if err != nil {
		return newNetworkError(err)
	}
	defer resp.Body.Close() //nolint:errcheck

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return newNetworkError(err)
	}

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return parseAPIError(respBody, resp.StatusCode)
	}

	if out != nil && resp.StatusCode != http.StatusNoContent {
		if err := json.Unmarshal(respBody, out); err != nil {
			return &AgentIdPError{
				Code:       "PARSE_ERROR",
				Message:    fmt.Sprintf("failed to parse response: %s", err.Error()),
				HTTPStatus: resp.StatusCode,
			}
		}
	}

	return nil
}
