/*
Copyright (c) Edgeless Systems GmbH

SPDX-License-Identifier: AGPL-3.0-only
*/

package license

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

const (
	// CommunityLicense is used by everyone who has not bought an enterprise license.
	CommunityLicense = "00000000-0000-0000-0000-000000000000"
	apiHost          = "license.confidential.cloud"
	licensePath      = "api/v1/license"
)

type (
	// Action performed by Constellation.
	Action string
)

const (
	// Init action denotes the initialization of a Constellation cluster.
	Init Action = "init"
	// test action is only to be used in testing.
	test Action = "test"
)

// Client interacts with the ES license server.
type Client struct {
	httpClient *http.Client
}

// NewClient creates a new client to interact with ES license server.
func NewClient() *Client {
	return &Client{
		httpClient: http.DefaultClient,
	}
}

// QuotaCheckRequest is JSON request to license server to check quota for a given license and action.
type QuotaCheckRequest struct {
	Action   Action `json:"action"`
	Provider string `json:"provider"`
	License  string `json:"license"`
}

// QuotaCheckResponse is JSON response by license server.
type QuotaCheckResponse struct {
	Quota int `json:"quota"`
}

// QuotaCheck for a given license and action, passed via CheckQuotaRequest.
func (c *Client) QuotaCheck(ctx context.Context, checkRequest QuotaCheckRequest) (QuotaCheckResponse, error) {
	reqBody, err := json.Marshal(checkRequest)
	if err != nil {
		return QuotaCheckResponse{}, fmt.Errorf("unable to marshal input: %w", err)
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, licenseURL().String(), bytes.NewBuffer(reqBody))
	if err != nil {
		return QuotaCheckResponse{}, fmt.Errorf("unable to create request: %w", err)
	}
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return QuotaCheckResponse{}, fmt.Errorf("unable to do request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return QuotaCheckResponse{}, fmt.Errorf("http error %d", resp.StatusCode)
	}

	responseContentType := resp.Header.Get("Content-Type")
	if responseContentType != "application/json" {
		return QuotaCheckResponse{}, fmt.Errorf("expected server JSON response but got '%s'", responseContentType)
	}

	var parsedResponse QuotaCheckResponse
	err = json.NewDecoder(resp.Body).Decode(&parsedResponse)
	if err != nil {
		return QuotaCheckResponse{}, fmt.Errorf("unable to parse response: %w", err)
	}

	return parsedResponse, nil
}

func licenseURL() *url.URL {
	return &url.URL{
		Scheme: "https",
		Host:   apiHost,
		Path:   licensePath,
	}
}

// QuotaChecker checks the vCPU quota for a given license.
type QuotaChecker interface {
	QuotaCheck(ctx context.Context, checkRequest QuotaCheckRequest) (QuotaCheckResponse, error)
}