package azuremonitor

import (
	"encoding/json"
	"fmt"
	"net/url"
	"path"

	"github.com/grafana/grafana-azure-sdk-go/v2/azcredentials"
	"github.com/grafana/grafana-azure-sdk-go/v2/azsettings"

	"github.com/grafana/grafana/pkg/tsdb/azuremonitor/types"
)

// Azure cloud query types
const (
	azureMonitor       = "Azure Monitor"
	azureLogAnalytics  = "Azure Log Analytics"
	azureResourceGraph = "Azure Resource Graph"
	azureTraces        = "Azure Traces"
	azurePortal        = "Azure Portal"
	traceExemplar      = "traceql"
)

func getAzureMonitorRoutes(settings *azsettings.AzureSettings, credentials azcredentials.AzureCredentials, jsonData json.RawMessage) (map[string]types.AzRoute, error) {
	azureCloud, err := azcredentials.GetAzureCloud(settings, credentials)
	if err != nil {
		return nil, err
	}

	if azureCloud == azsettings.AzureCustomized {
		routes, err := getCustomizedCloudRoutes(jsonData)
		if err != nil {
			return nil, err
		}
		return routes, nil
	}

	cloudSettings, err := settings.GetCloud(azureCloud)
	if err != nil {
		return nil, err
	}

	resourceManagerUrl, ok := cloudSettings.Properties["resourceManager"]
	if !ok {
		err := fmt.Errorf("the Azure cloud '%s' doesn't have configuration for Azure Resource Manager", azureCloud)
		return nil, err
	}
	resourceManagerScopes, err := audienceToScopes(resourceManagerUrl)
	if err != nil {
		return nil, err
	}
	resourceManagerRoute := types.AzRoute{
		URL:     resourceManagerUrl,
		Scopes:  resourceManagerScopes,
		Headers: map[string]string{"x-ms-app": "Grafana"},
	}
	logAnalyticsUrl, ok := cloudSettings.Properties["logAnalytics"]
	if !ok {
		err := fmt.Errorf("the Azure cloud '%s' doesn't have configuration for Azure Log Analytics", azureCloud)
		return nil, err
	}
	logAnalyticsScopes, err := audienceToScopes(logAnalyticsUrl)
	if err != nil {
		return nil, err
	}
	logAnalyticsRoute := types.AzRoute{
		URL:     logAnalyticsUrl,
		Scopes:  logAnalyticsScopes,
		Headers: map[string]string{"x-ms-app": "Grafana", "Cache-Control": "public, max-age=60"},
	}
	portalUrl, ok := cloudSettings.Properties["portal"]
	if !ok {
		err := fmt.Errorf("the Azure cloud '%s' doesn't have configuration for Azure Portal", azureCloud)
		return nil, err
	}
	portalRoute := types.AzRoute{
		URL: portalUrl,
	}

	routes := map[string]types.AzRoute{
		azureMonitor:       resourceManagerRoute,
		azureLogAnalytics:  logAnalyticsRoute,
		azureResourceGraph: resourceManagerRoute,
		azureTraces:        logAnalyticsRoute,
		traceExemplar:      logAnalyticsRoute,
		azurePortal:        portalRoute,
	}

	return routes, nil
}

func getCustomizedCloudRoutes(jsonData json.RawMessage) (map[string]types.AzRoute, error) {
	customizedCloudSettings := types.AzureMonitorCustomizedCloudSettings{}
	err := json.Unmarshal(jsonData, &customizedCloudSettings)
	if err != nil {
		return nil, fmt.Errorf("error getting customized cloud settings: %w", err)
	}

	if customizedCloudSettings.CustomizedRoutes == nil {
		return nil, fmt.Errorf("unable to instantiate routes, customizedRoutes must be set")
	}

	azureRoutes := customizedCloudSettings.CustomizedRoutes
	return azureRoutes, nil
}

func audienceToScopes(audience string) ([]string, error) {
	resourceId, err := url.Parse(audience)
	if err != nil || resourceId.Scheme == "" || resourceId.Host == "" {
		err = fmt.Errorf("endpoint resource ID (audience) '%s' invalid", audience)
		return nil, err
	}

	resourceId.Path = path.Join(resourceId.Path, ".default")
	scopes := []string{resourceId.String()}
	return scopes, nil
}
