package capability

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

type ProjectionClient struct {
	BaseURL    string
	Token      string
	HTTPClient *http.Client
}

func NewProjectionClient(baseURL string, token string, httpClient *http.Client) *ProjectionClient {
	return &ProjectionClient{
		BaseURL:    strings.TrimRight(strings.TrimSpace(baseURL), "/"),
		Token:      strings.TrimSpace(token),
		HTTPClient: httpClient,
	}
}

func (client *ProjectionClient) UpsertAuthorityProjection(ctx context.Context, projection AuthorityProjection) error {
	if client == nil {
		return fmt.Errorf("maas projection client is not configured")
	}
	baseURL := strings.TrimRight(strings.TrimSpace(client.BaseURL), "/")
	if baseURL == "" {
		return fmt.Errorf("AGENTTEAM_MAAS_BASE_URL is required")
	}
	token := strings.TrimSpace(client.Token)
	if token == "" {
		return fmt.Errorf("AGENTTEAM_MAAS_INTERNAL_BEARER_TOKEN is required")
	}
	projectionID := strings.TrimSpace(projection.ID)
	if projectionID == "" {
		projectionID = ProjectionID(projection)
	}

	payload := authorityProjectionRequest{
		OrganizationID:      projection.OrganizationID,
		WorkspaceID:         projection.WorkspaceID,
		AgentID:             projection.AgentID,
		MemberID:            projection.MemberID,
		MachineID:           projection.MachineID,
		RuntimeSessionID:    projection.RuntimeSessionID,
		AuthorityGeneration: projection.AuthorityGeneration,
		OrganizationStatus:  projection.OrganizationStatus,
		WorkspaceStatus:     projection.WorkspaceStatus,
		AgentStatus:         projection.AgentStatus,
		RuntimeStatus:       projection.RuntimeStatus,
		RevokedAt:           projection.RevokedAt,
		EmergencyStoppedAt:  projection.EmergencyStoppedAt,
	}
	var body bytes.Buffer
	if err := json.NewEncoder(&body).Encode(payload); err != nil {
		return fmt.Errorf("encode maas authority projection %s: %w", projectionID, err)
	}

	endpoint := baseURL + "/internal/maas/v1/authority-projections/" + url.PathEscape(projectionID)
	request, err := http.NewRequestWithContext(ctx, http.MethodPut, endpoint, &body)
	if err != nil {
		return fmt.Errorf("build maas authority projection request %s: %w", projectionID, err)
	}
	request.Header.Set("Authorization", "Bearer "+token)
	request.Header.Set("Content-Type", "application/json")

	httpClient := client.HTTPClient
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	response, err := httpClient.Do(request)
	if err != nil {
		return fmt.Errorf("push maas authority projection %s: %w", projectionID, err)
	}
	defer response.Body.Close()

	if response.StatusCode >= 200 && response.StatusCode < 300 {
		return nil
	}
	responseBody, _ := io.ReadAll(io.LimitReader(response.Body, 2048))
	return fmt.Errorf("push maas authority projection %s: HTTP %d: %s", projectionID, response.StatusCode, strings.TrimSpace(string(responseBody)))
}

type authorityProjectionRequest struct {
	OrganizationID      string     `json:"organization_id"`
	WorkspaceID         string     `json:"workspace_id"`
	AgentID             string     `json:"agent_id"`
	MemberID            string     `json:"member_id"`
	MachineID           string     `json:"machine_id"`
	RuntimeSessionID    string     `json:"runtime_session_id"`
	AuthorityGeneration int64      `json:"authority_generation"`
	OrganizationStatus  string     `json:"organization_status"`
	WorkspaceStatus     string     `json:"workspace_status"`
	AgentStatus         string     `json:"agent_status"`
	RuntimeStatus       string     `json:"runtime_status"`
	RevokedAt           *time.Time `json:"revoked_at"`
	EmergencyStoppedAt  *time.Time `json:"emergency_stopped_at"`
}
