package cloudlink

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

	"github.com/warmbly/warmbly/internal/errx"
)

// client is the outbound-only HTTP client to the cloud's pool-link API.
type client struct {
	baseURL string
	token   string
	version string
	http    *http.Client
}

func newClient(baseURL, token, version string) *client {
	return &client{
		baseURL: strings.TrimRight(strings.TrimSpace(baseURL), "/"),
		token:   token,
		version: version,
		http:    &http.Client{Timeout: 20 * time.Second},
	}
}

// remoteError is the cloud's error envelope, re-surfaced with its own code.
type remoteError struct {
	Error     string `json:"error"`
	Message   string `json:"message"`
	Code      string `json:"code"`
	RequestID string `json:"request_id"`
}

func (c *client) do(ctx context.Context, method, path string, body any, out any) *errx.Error {
	var buf io.Reader
	if body != nil {
		raw, err := json.Marshal(body)
		if err != nil {
			return errx.InternalError()
		}
		buf = bytes.NewReader(raw)
	}
	req, err := http.NewRequestWithContext(ctx, method, c.baseURL+"/v1/pool-link"+path, buf)
	if err != nil {
		return errx.InternalError()
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("User-Agent", "warmbly-cloudlink/"+c.version)
	if c.token != "" {
		req.Header.Set("Authorization", "Bearer "+c.token)
	}
	if c.version != "" {
		req.Header.Set("X-Warmbly-Instance-Version", c.version)
	}
	res, err := c.http.Do(req)
	if err != nil {
		return errx.NewWithIdentifier(errx.ServiceUnavailable, "cloud_link_unreachable", fmt.Sprintf("Warmbly Cloud could not be reached: %v", err))
	}
	defer res.Body.Close()
	raw, _ := io.ReadAll(io.LimitReader(res.Body, 4<<20))
	if res.StatusCode >= 400 {
		var re remoteError
		_ = json.Unmarshal(raw, &re)
		code := errx.Code(res.StatusCode)
		if re.Message == "" {
			re.Message = fmt.Sprintf("Warmbly Cloud answered %d", res.StatusCode)
		}
		if re.Code == "" {
			re.Code = "cloud_link_remote"
		}
		return errx.NewWithIdentifier(code, re.Code, re.Message)
	}
	if out != nil && len(raw) > 0 {
		if err := json.Unmarshal(raw, out); err != nil {
			return errx.NewWithIdentifier(errx.ServiceUnavailable, "cloud_link_bad_response", "Warmbly Cloud returned an unreadable response.")
		}
	}
	return nil
}
