package operator

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

// apiOperator holds shared HTTP client state for all API sub-operators.
type apiOperator struct {
	baseURL string
	apiKey  string
	client  *http.Client
}

func newAPIOperator(baseURL, apiKey string) Operator {
	return &apiOperator{
		baseURL: baseURL,
		apiKey:  apiKey,
		client:  &http.Client{Timeout: 30 * time.Second},
	}
}

// ---------------------------------------------------------------------------
// HTTP helpers
// ---------------------------------------------------------------------------

func (a *apiOperator) newRequest(method, path string, body interface{}) (*http.Request, error) {
	var bodyReader io.Reader
	if body != nil {
		data, err := json.Marshal(body)
		if err != nil {
			return nil, fmt.Errorf("marshal request body: %w", err)
		}
		bodyReader = bytes.NewReader(data)
	}

	req, err := http.NewRequest(method, a.baseURL+path, bodyReader)
	if err != nil {
		return nil, fmt.Errorf("create request: %w", err)
	}

	req.Header.Set("X-Api-Key", a.apiKey)
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	return req, nil
}

func (a *apiOperator) doGet(path string, result interface{}) error {
	req, err := a.newRequest(http.MethodGet, path, nil)
	if err != nil {
		return err
	}
	return a.doRequest(req, result)
}

func (a *apiOperator) doPost(path string, body interface{}, result interface{}) error {
	req, err := a.newRequest(http.MethodPost, path, body)
	if err != nil {
		return err
	}
	return a.doRequest(req, result)
}

func (a *apiOperator) doPut(path string, body interface{}, result interface{}) error {
	req, err := a.newRequest(http.MethodPut, path, body)
	if err != nil {
		return err
	}
	return a.doRequest(req, result)
}

func (a *apiOperator) doDelete(path string) error {
	req, err := a.newRequest(http.MethodDelete, path, nil)
	if err != nil {
		return err
	}
	return a.doRequest(req, nil)
}

func (a *apiOperator) doRequest(req *http.Request, result interface{}) error {
	resp, err := a.client.Do(req)
	if err != nil {
		return fmt.Errorf("daemon request failed: %w", err)
	}
	defer resp.Body.Close()

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

	if resp.StatusCode >= 400 {
		return translateError(resp.StatusCode, body)
	}

	if result != nil && len(body) > 0 {
		if err := json.Unmarshal(body, result); err != nil {
			return fmt.Errorf("parse response: %w", err)
		}
	}
	return nil
}

// ---------------------------------------------------------------------------
// Error translation
// ---------------------------------------------------------------------------

type apiError struct {
	StatusCode int
	Message    string
}

func (e *apiError) Error() string {
	return e.Message
}

func translateError(statusCode int, body []byte) error {
	var errResp struct {
		Error string `json:"error"`
	}
	if json.Unmarshal(body, &errResp) == nil && errResp.Error != "" {
		return &apiError{StatusCode: statusCode, Message: errResp.Error}
	}
	return &apiError{
		StatusCode: statusCode,
		Message:    fmt.Sprintf("daemon error (HTTP %d)", statusCode),
	}
}

func isNotFound(err error) bool {
	var apiErr *apiError
	return errors.As(err, &apiErr) && apiErr.StatusCode == 404
}

func isConflict(err error) bool {
	var apiErr *apiError
	return errors.As(err, &apiErr) && apiErr.StatusCode == 409
}

func isUnprocessable(err error) bool {
	var apiErr *apiError
	return errors.As(err, &apiErr) && apiErr.StatusCode == 422
}
