package cloudflare

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

const apiBase = "https://api.cloudflare.com/client/v4"

// Client handles Cloudflare API calls.
type Client struct {
	apiToken   string
	httpClient *http.Client
	logger     *log.Logger
	debug      bool
}

// New creates a Cloudflare API client.
func New(apiToken string, logger *log.Logger, debug bool) *Client {
	return &Client{
		apiToken:   apiToken,
		httpClient: &http.Client{Timeout: 15 * time.Second},
		logger:     logger,
		debug:      debug,
	}
}

// IsConfigured returns true if a CF API token is set.
func (c *Client) IsConfigured() bool {
	return c.apiToken != ""
}

// apiResponse is the generic Cloudflare API response wrapper.
type apiResponse struct {
	Success  bool            `json:"success"`
	Errors   []apiError      `json:"errors"`
	Messages []apiMessage    `json:"messages"`
	Result   json.RawMessage `json:"result"`
}

type apiError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type apiMessage struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

// do performs an HTTP request to the Cloudflare API and decodes the response.
func (c *Client) do(ctx context.Context, method, path string, body interface{}) (*apiResponse, error) {
	var bodyReader io.Reader
	if body != nil {
		data, err := json.Marshal(body)
		if err != nil {
			return nil, fmt.Errorf("marshal request: %w", err)
		}
		bodyReader = bytes.NewReader(data)
		if c.debug {
			c.logger.Printf("[DEBUG] [cloudflare] %s %s body=%s", method, path, string(data))
		}
	} else if c.debug {
		c.logger.Printf("[DEBUG] [cloudflare] %s %s", method, path)
	}

	url := apiBase + path
	req, err := http.NewRequestWithContext(ctx, method, url, bodyReader)
	if err != nil {
		return nil, fmt.Errorf("create request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+c.apiToken)
	req.Header.Set("Content-Type", "application/json")

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

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

	if c.debug {
		c.logger.Printf("[DEBUG] [cloudflare] Response %d: %s", resp.StatusCode, string(respBody))
	}

	var apiResp apiResponse
	if err := json.Unmarshal(respBody, &apiResp); err != nil {
		return nil, fmt.Errorf("decode response (status %d): %w", resp.StatusCode, err)
	}

	if !apiResp.Success {
		msg := "unknown error"
		if len(apiResp.Errors) > 0 {
			msg = apiResp.Errors[0].Message
			for _, e := range apiResp.Errors[1:] {
				msg += "; " + e.Message
			}
		}
		return &apiResp, fmt.Errorf("cloudflare API error (status %d): %s", resp.StatusCode, msg)
	}

	return &apiResp, nil
}
