// Copyright 2024 Cloudbase Solutions SRL
//
//    Licensed under the Apache License, Version 2.0 (the "License"); you may
//    not use this file except in compliance with the License. You may obtain
//    a copy of the License at
//
//         http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
//    License for the specific language governing permissions and limitations
//    under the License.

package scalesets

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

	"github.com/cloudbase/garm/params"
)

func (s *ScaleSetClient) getActionServiceInfo(ctx context.Context) (params.ActionsServiceAdminInfoResponse, error) {
	regPath := "/actions/runner-registration"
	baseURL := s.ghCli.GithubBaseURL()
	url, err := baseURL.Parse(regPath)
	if err != nil {
		return params.ActionsServiceAdminInfoResponse{}, fmt.Errorf("failed to parse url: %w", err)
	}

	entity := s.ghCli.GetEntity()
	body := params.ActionsServiceAdminInfoRequest{
		URL:         entity.ForgeURL(),
		RunnerEvent: "register",
	}

	buf := &bytes.Buffer{}
	enc := json.NewEncoder(buf)
	enc.SetEscapeHTML(false)

	if err := enc.Encode(body); err != nil {
		return params.ActionsServiceAdminInfoResponse{}, err
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, url.String(), buf)
	if err != nil {
		return params.ActionsServiceAdminInfoResponse{}, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", fmt.Sprintf("RemoteAuth %s", *s.runnerRegistrationToken.Token))

	resp, err := s.Do(req)
	if err != nil {
		return params.ActionsServiceAdminInfoResponse{}, fmt.Errorf("failed to get actions service admin info: %w", err)
	}
	defer resp.Body.Close()

	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return params.ActionsServiceAdminInfoResponse{}, fmt.Errorf("failed to read response body: %w", err)
	}
	data = bytes.TrimPrefix(data, []byte("\xef\xbb\xbf"))

	var info params.ActionsServiceAdminInfoResponse
	if err := json.Unmarshal(data, &info); err != nil {
		return params.ActionsServiceAdminInfoResponse{}, fmt.Errorf("failed to decode response: %w", err)
	}

	return info, nil
}

func (s *ScaleSetClient) ensureAdminInfo(ctx context.Context) error {
	s.mux.Lock()
	defer s.mux.Unlock()

	var expiresAt time.Time
	if s.runnerRegistrationToken != nil {
		expiresAt = s.runnerRegistrationToken.GetExpiresAt().Time
	}

	now := time.Now().UTC().Add(2 * time.Minute)
	if now.After(expiresAt) || s.runnerRegistrationToken == nil {
		token, _, err := s.ghCli.CreateEntityRegistrationToken(ctx)
		if err != nil {
			return fmt.Errorf("failed to fetch runner registration token: %w", err)
		}
		s.runnerRegistrationToken = token
	}

	if s.actionsServiceInfo == nil || s.actionsServiceInfo.ExpiresIn(2*time.Minute) {
		info, err := s.getActionServiceInfo(ctx)
		if err != nil {
			return fmt.Errorf("failed to get action service info: %w", err)
		}
		s.actionsServiceInfo = &info
	}

	return nil
}
