package cloudimpl

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"slices"
	"strings"

	"github.com/pingcap/dbaas/billing2/constant"

	"github.com/pingcap/dbaas/billing2/forms"

	"github.com/pingcap/dbaas/billing2/config"

	"github.com/pingcap/dbaas/billing2/utils/httpclient"

	"github.com/pingcap/dbaas/billing2/utils/log"
	"go.uber.org/zap"
)

type AzureMarketplaceImpl struct {
}

type TokenResponse struct {
	AccessToken string `json:"access_token"`
	TokenType   string `json:"token_type"`
	ExpiresIn   int    `json:"expires_in"`
}

func (m *AzureMarketplaceImpl) GetToken() (string, error) {

	urlAzure := fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", config.Get().GetString(config.AZUREMarketplaceTenantID))

	formData := url.Values{}
	formData.Set("client_id", config.Get().GetString(config.AZUREMarketplaceClientID))
	formData.Set("client_secret", config.Get().GetString(config.AZUREMarketplaceClientSecret))
	formData.Set("grant_type", "client_credentials")
	formData.Set("scope", "20e940b3-4c77-4b0b-9a53-9e16a1b010a7/.default")

	req, err := http.NewRequest("POST", urlAzure, strings.NewReader(formData.Encode()))
	if err != nil {
		log.Error("Error creating request:", zap.Error(err))
		return "", err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Error("Error sending request:", zap.Error(err))
		return "", err

	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Error("Error reading response body:", zap.Error(err))
		return "", err
	}

	//log.Info("HTTP  :", zap.String("Status", resp.Status), zap.String("Response Body", string(body)))

	var tokenResponse TokenResponse
	if resp.StatusCode == http.StatusOK {
		err = json.Unmarshal(body, &tokenResponse)
		if err != nil {
			log.Error("Error decoding JSON response:", zap.Error(err))
			return "", err

		}
	}
	return tokenResponse.AccessToken, nil
}

func (m *AzureMarketplaceImpl) GetSubscription(subscriptionID string) (forms.SubscriptionDetails, error) {
	apiPath := fmt.Sprintf("/api/saas/subscriptions/%s?api-version=2018-08-31", subscriptionID)
	var subscriptionDetails forms.SubscriptionDetails

	err := m.GetAPIClient().Request("GET", apiPath, nil, nil, &subscriptionDetails)
	if err != nil {
		log.Error("Error decoding JSON response:", zap.Error(err))

		return subscriptionDetails, err
	}
	log.Info("GetSubscription :", zap.Any("subscriptionDetails", subscriptionDetails))

	return subscriptionDetails, nil
}

func (m *AzureMarketplaceImpl) GetSubscriptionList() (subscriptionList forms.SubscriptionList, err error) {
	var totalList forms.SubscriptionList
	apiPath := "/api/saas/subscriptions?api-version=2018-08-31"

	for {
		var page forms.SubscriptionList
		err := m.GetAPIClient().Request("GET", apiPath, nil, nil, &page)
		if err != nil {
			log.Error("Error getting subscription page:", zap.Error(err))
			return totalList, err
		}
		for _, subscription := range page.Subscriptions {
			if !slices.Contains(config.Get().GetStringSlice(config.AzureOfferList), subscription.OfferId) {
				continue
			}
			totalList.Subscriptions = append(totalList.Subscriptions, subscription)
		}
		if page.NextLink == "" {
			break
		}
		nextURL, err := url.Parse(page.NextLink)
		if err != nil {
			log.Error("Error parsing nextLink URL:", zap.Error(err), zap.String("nextLink", page.NextLink))
			return totalList, err
		}

		apiPath = nextURL.Path
		if nextURL.RawQuery != "" {
			apiPath += "?" + nextURL.RawQuery
		}
	}
	//pp.Print(totalList)
	log.Info("GetSubscription total count:", zap.Int("count", len(totalList.Subscriptions)))
	return totalList, nil
}
func (m *AzureMarketplaceImpl) ActivateSubscription(subscriptionID string) error {
	apiPath := fmt.Sprintf("/api/saas/subscriptions/%s/activate?api-version=2018-08-31", subscriptionID)

	err := m.GetAPIClient().Request("POST", apiPath, nil, nil, nil)
	if err != nil {
		log.Error("Error decoding JSON response:", zap.Error(err))

		return err
	}
	log.Info("ActivateSubscription success :", zap.String("subscriptionID", subscriptionID))

	return nil
}
func (m *AzureMarketplaceImpl) CancelSubscription(subscriptionID string) error {
	apiPath := fmt.Sprintf("/api/saas/subscriptions/%s?api-version=2018-08-31", subscriptionID)

	err := m.GetAPIClient().Request("DELETE", apiPath, nil, nil, nil)
	if err != nil {
		log.Error("Error decoding JSON response:", zap.Error(err))

		return err
	}
	log.Info("CancelSubscription success:", zap.String("subscriptionID", subscriptionID))

	return nil
}
func (m *AzureMarketplaceImpl) GetSubscriptionsByAzureSubscriptionID(subscriptionID string, isSubscribed bool) (forms.SubscriptionList, error) {
	var resList forms.SubscriptionList

	subscriptionH, err := m.GetSubscription(subscriptionID)
	if err != nil {
		log.Error("GetSubscription :", zap.Error(err))

		return resList, err
	}
	subscriptionList, err := m.GetSubscriptionList()
	if err != nil {
		log.Error("GetSubscriptionByMarketplaceID :", zap.Error(err))

		return resList, err
	}
	for _, subscription := range subscriptionList.Subscriptions {
		if subscription.Purchaser.TenantId == subscriptionH.Purchaser.TenantId {
			if isSubscribed && subscription.SaasSubscriptionStatus != constant.AzureSubscriptionStatusSubscribed {
				continue
			}
			if subscription.SaasSubscriptionStatus == constant.AzureSubscriptionStatusSubscribed && subscription.Id != subscriptionID {
				continue
			}
			resList.Subscriptions = append(resList.Subscriptions, subscription)
		}

	}
	return resList, nil
}
func (m *AzureMarketplaceImpl) GetSubscriptionListByStatus(Status map[string]bool) (map[string]string, error) {
	subscriptionList, err := m.GetSubscriptionList()
	if err != nil {
		log.Error("GetSubscriptionByMarketplaceID :", zap.Error(err))

		return nil, err
	}
	res := map[string]string{}
	for _, subscription := range subscriptionList.Subscriptions {
		if Status[subscription.SaasSubscriptionStatus] {
			res[subscription.Id] = subscription.SaasSubscriptionStatus
		}

	}
	return res, nil
}

func (m *AzureMarketplaceImpl) azureAuth(req *http.Request) error {
	token, err := m.GetToken()
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	return nil
}
func (m *AzureMarketplaceImpl) GetAPIClient() httpclient.HTTPClient {
	return httpclient.NewClient(m.azureAuth, "https://marketplaceapi.microsoft.com", "/")
}
