package diag

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"
)

// maxReportSize bounds a stored report, on both the sending and the
// receiving side.
const maxReportSize = 4 << 20 // 4 MB

// Upload posts a report to a relay's diagnostics endpoint and returns the URL
// it can be fetched from.
//
// The point is getting a report off a machine that is awkward to copy from —
// a headless NAS, a Windows box mid-debugging — without pasting thousands of
// lines by hand.
func Upload(relayURL, reportID string, report *Report) (string, error) {
	data, err := report.JSON()
	if err != nil {
		return "", fmt.Errorf("encoding report: %w", err)
	}
	if len(data) > maxReportSize {
		return "", fmt.Errorf("report is %d bytes, over the %d byte limit", len(data), maxReportSize)
	}

	target, err := DiagURL(relayURL, reportID)
	if err != nil {
		return "", err
	}

	req, err := http.NewRequest(http.MethodPut, target, bytes.NewReader(data))
	if err != nil {
		return "", fmt.Errorf("building request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("uploading to %s: %w", target, err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
		body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
		return "", fmt.Errorf("relay refused the report: %s: %s",
			resp.Status, strings.TrimSpace(string(body)))
	}

	return target, nil
}

// DiagURL builds the diagnostics URL for a report ID on a relay.
//
// It accepts the same address forms the client's relay setting does, so the
// user does not have to remember a second syntax.
func DiagURL(relayURL, reportID string) (string, error) {
	if reportID == "" {
		return "", fmt.Errorf("a report ID is required")
	}
	if strings.ContainsAny(reportID, "/?#") {
		return "", fmt.Errorf("report ID must not contain /, ? or #")
	}

	base := strings.TrimSuffix(strings.TrimSpace(relayURL), "/")
	base = strings.TrimSuffix(base, "/ws")

	switch {
	case strings.HasPrefix(base, "ws://"):
		base = "http://" + strings.TrimPrefix(base, "ws://")
	case strings.HasPrefix(base, "wss://"):
		base = "https://" + strings.TrimPrefix(base, "wss://")
	case strings.HasPrefix(base, "http://"), strings.HasPrefix(base, "https://"):
		// already fine
	default:
		base = "http://" + base
	}

	return base + "/diag/" + reportID, nil
}

// RetentionNote describes how long an uploaded report survives on the relay.
// Kept here so the client can say so without importing the relay package.
const RetentionNote = "24 hours"
