package clinic_event

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

var globalEventCli *EventClient

// EventCLient
type EventClient struct {
	client              *http.Client
	activityHubEndpoint string
	source              string
	sourceKey           string
	clinicEndpoint      string
	clinicToken         string
}

func InitEventClient(endpoint, source, sourceKey, clinicEndpoint, clinicToken string) error {
	if globalEventCli != nil {
		return nil
	}

	if source == "" || sourceKey == "" {
		return errors.New("init event client failed: source and source-key must be specified")
	}

	client := &http.Client{}
	globalEventCli = &EventClient{
		client:              client,
		activityHubEndpoint: endpoint,
		source:              source,
		sourceKey:           sourceKey,
		clinicEndpoint:      clinicEndpoint,
		clinicToken:         clinicToken,
	}
	return nil
}

type EventRequest struct {
	Type            string                 `json:"type"`
	Name            string                 `json:"name"`
	SeverityLevel   int                    `json:"severity"`
	ApplicationID   string                 `json:"application_id"` // tenant id
	ConceptID       string                 `json:"concept_id"`     // project id
	TargetID        string                 `json:"target_id"`      //cluster id
	Catalog         string                 `json:"catalog"`        // event group
	CalibrationTime uint64                 `json:"calibration_time"`
	Author          string                 `json:"author"`
	ContextID       string                 `json:"context_id"`
	Context         []string               `json:"context"`
	Payload         map[string]interface{} `json:"payload"`
}

// SendEvent
func SendEvent(request EventRequest) error {

	if globalEventCli == nil || globalEventCli.activityHubEndpoint == "" {
		return errors.New("global event client is not initialized")
	}
	if request.Type == "" {
		request.Type = "event"
	}

	data, err := json.Marshal(request)
	if err != nil {
		return err
	}
	req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/activity", globalEventCli.activityHubEndpoint), bytes.NewBuffer(data))
	if err != nil {
		return err
	}

	// add auth header
	req.Header.Add(XActivityHubSource, globalEventCli.source)
	req.Header.Add(XActivityHubKey, globalEventCli.sourceKey)

	resp, err := globalEventCli.client.Do(req)
	if err != nil {
		return err
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	if resp.StatusCode != http.StatusAccepted {
		body, err := io.ReadAll(resp.Body)
		return fmt.Errorf("send event failed, got status %s, error %s %v", resp.Status, string(body), err)
	}

	// register cluster job
	if strings.HasSuffix(request.Name, "apply") {
		return registerClinicJob(request.TargetID, request.ApplicationID,
			request.ContextID, strings.Split(request.Name, ":")[0], request.Payload["summary"].(string))
	}

	return nil
}

// registerClinicJob
func registerClinicJob(clusterID, tenantID, contextID, jobType, summary string) error {

	if globalEventCli == nil || globalEventCli.clinicEndpoint == "" {
		return errors.New("clinic endpoint must be specified")
	}

	path := fmt.Sprintf("%s/clinic/api/v1/activityhub/applications/%s/targets/%s/contexts/%s?type=%s&summary=%s",
		globalEventCli.clinicEndpoint, tenantID, clusterID, contextID, jobType, summary)
	u, _ := url.Parse(path)
	q := u.Query()
	u.RawQuery = q.Encode()

	req, err := http.NewRequest(http.MethodPost, u.String(), nil)
	if err != nil {
		return err
	}

	// add auth header
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", globalEventCli.clinicToken))

	resp, err := globalEventCli.client.Do(req)
	if err != nil {
		return err
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	if resp.StatusCode != http.StatusAccepted {
		body, err := io.ReadAll(resp.Body)
		return fmt.Errorf("register clinic job failed, got status %s, error %s %v", resp.Status, string(body), err)
	}
	return nil
}
