package wsclient

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

	"gitea.dcglab.co.uk/steve/restic-manager/internal/api"
)

// EnrollRequest is what we POST to /api/agents/enroll.
type EnrollRequest struct {
	Token         string       `json:"token"`
	HostName      string       `json:"hostname"`
	OS            api.HostOS   `json:"os"`
	Arch          api.HostArch `json:"arch"`
	AgentVersion  string       `json:"agent_version"`
	ResticVersion string       `json:"restic_version"`
}

// EnrollResponse is what the server hands back.
type EnrollResponse struct {
	HostID        string `json:"host_id"`
	AgentToken    string `json:"agent_token"`
	CertPinSHA256 string `json:"cert_pin_sha256,omitempty"`
}

// Enroll exchanges a one-time enrollment token for persistent agent
// credentials. Called by the install script on first run.
func Enroll(ctx context.Context, serverURL string, req EnrollRequest) (*EnrollResponse, error) {
	body, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("agent enroll: marshal: %w", err)
	}
	postURL := strings.TrimRight(serverURL, "/") + "/api/agents/enroll"

	httpReq, err := stdhttp.NewRequestWithContext(ctx, stdhttp.MethodPost, postURL, bytes.NewReader(body))
	if err != nil {
		return nil, fmt.Errorf("agent enroll: build request: %w", err)
	}
	httpReq.Header.Set("Content-Type", "application/json")

	client := &stdhttp.Client{Timeout: 30 * time.Second}
	res, err := client.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("agent enroll: post: %w", err)
	}
	defer func() { _ = res.Body.Close() }()
	rawRes, _ := io.ReadAll(res.Body)
	if res.StatusCode != stdhttp.StatusCreated {
		return nil, fmt.Errorf("agent enroll: server returned %d: %s",
			res.StatusCode, rawRes)
	}
	var er EnrollResponse
	if err := json.Unmarshal(rawRes, &er); err != nil {
		return nil, fmt.Errorf("agent enroll: parse response: %w", err)
	}
	if er.AgentToken == "" || er.HostID == "" {
		return nil, fmt.Errorf("agent enroll: incomplete response: %+v", er)
	}
	return &er, nil
}
