/*
Copyright (c) Edgeless Systems GmbH

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

package gcpshared

import (
	"fmt"
	"net/url"
)

// ServiceAccountKey is a GCP service account key.
type ServiceAccountKey struct {
	Type                    string `json:"type"`
	ProjectID               string `json:"project_id"`
	PrivateKeyID            string `json:"private_key_id"`
	PrivateKey              string `json:"private_key"`
	ClientEmail             string `json:"client_email"`
	ClientID                string `json:"client_id"`
	AuthURI                 string `json:"auth_uri"`
	TokenURI                string `json:"token_uri"`
	AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"`
	ClientX509CertURL       string `json:"client_x509_cert_url"`
}

// ServiceAccountKeyFromURI parses ServiceAccountKey from URI.
func ServiceAccountKeyFromURI(serviceAccountURI string) (ServiceAccountKey, error) {
	uri, err := url.Parse(serviceAccountURI)
	if err != nil {
		return ServiceAccountKey{}, err
	}
	if uri.Scheme != "serviceaccount" {
		return ServiceAccountKey{}, fmt.Errorf("invalid service account URI: invalid scheme: %s", uri.Scheme)
	}
	if uri.Host != "gcp" {
		return ServiceAccountKey{}, fmt.Errorf("invalid service account URI: invalid host: %s", uri.Host)
	}
	query := uri.Query()
	if query.Get("type") == "" {
		return ServiceAccountKey{}, fmt.Errorf("invalid service account URI: missing parameter \"type\": %s", uri)
	}
	if query.Get("project_id") == "" {
		return ServiceAccountKey{}, fmt.Errorf("invalid service account URI: missing parameter \"project_id\": %s", uri)
	}
	if query.Get("private_key_id") == "" {
		return ServiceAccountKey{}, fmt.Errorf("invalid service account URI: missing parameter \"private_key_id\": %s", uri)
	}
	if query.Get("private_key") == "" {
		return ServiceAccountKey{}, fmt.Errorf("invalid service account URI: missing parameter \"private_key\": %s", uri)
	}
	if query.Get("client_email") == "" {
		return ServiceAccountKey{}, fmt.Errorf("invalid service account URI: missing parameter \"client_email\": %s", uri)
	}
	if query.Get("client_id") == "" {
		return ServiceAccountKey{}, fmt.Errorf("invalid service account URI: missing parameter \"client_id\": %s", uri)
	}
	if query.Get("token_uri") == "" {
		return ServiceAccountKey{}, fmt.Errorf("invalid service account URI: missing parameter \"token_uri\": %s", uri)
	}
	if query.Get("auth_provider_x509_cert_url") == "" {
		return ServiceAccountKey{}, fmt.Errorf("invalid service account URI: missing parameter \"auth_provider_x509_cert_url\": %s", uri)
	}
	if query.Get("client_x509_cert_url") == "" {
		return ServiceAccountKey{}, fmt.Errorf("invalid service account URI: missing parameter \"client_x509_cert_url\": %s", uri)
	}
	return ServiceAccountKey{
		Type:                    query.Get("type"),
		ProjectID:               query.Get("project_id"),
		PrivateKeyID:            query.Get("private_key_id"),
		PrivateKey:              query.Get("private_key"),
		ClientEmail:             query.Get("client_email"),
		ClientID:                query.Get("client_id"),
		AuthURI:                 query.Get("auth_uri"),
		TokenURI:                query.Get("token_uri"),
		AuthProviderX509CertURL: query.Get("auth_provider_x509_cert_url"),
		ClientX509CertURL:       query.Get("client_x509_cert_url"),
	}, nil
}

// ToCloudServiceAccountURI converts the ServiceAccountKey into a cloud service account URI.
func (k ServiceAccountKey) ToCloudServiceAccountURI() string {
	query := url.Values{}
	query.Add("type", k.Type)
	query.Add("project_id", k.ProjectID)
	query.Add("private_key_id", k.PrivateKeyID)
	query.Add("private_key", k.PrivateKey)
	query.Add("client_email", k.ClientEmail)
	query.Add("client_id", k.ClientID)
	query.Add("auth_uri", k.AuthURI)
	query.Add("token_uri", k.TokenURI)
	query.Add("auth_provider_x509_cert_url", k.AuthProviderX509CertURL)
	query.Add("client_x509_cert_url", k.ClientX509CertURL)
	uri := url.URL{
		Scheme:   "serviceaccount",
		Host:     "gcp",
		RawQuery: query.Encode(),
	}
	return uri.String()
}