// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package auth

import (
	"context"
	"fmt"
	"os"

	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
	"github.com/opentofu/opentofu/internal/httpclient"
	"github.com/opentofu/opentofu/internal/tfdiags"
)

type ADOAuthConfig struct {
	ADOServiceConnectionId string
}

type adoAuth struct{}

var _ AuthMethod = &adoAuth{}

func (cred *adoAuth) Name() string {
	return "Azure DevOps Auth"
}

func (cred *adoAuth) Construct(ctx context.Context, config *Config) (azcore.TokenCredential, error) {
	clientId, err := consolidateClientId(config)
	if err != nil {
		return nil, err
	}

	return azidentity.NewAzurePipelinesCredential(
		config.TenantID,
		clientId,
		config.ADOServiceConnectionId,
		config.OIDCRequestToken,
		&azidentity.AzurePipelinesCredentialOptions{
			ClientOptions: clientOptions(httpclient.New(ctx), config.CloudConfig),
		},
	)
}

func (cred *adoAuth) Validate(ctx context.Context, config *Config) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics
	if !config.UseOIDC || config.ADOServiceConnectionId == "" {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Invalid Azure DevOps Auth",
			"To use Azure DevOps Auth, use_oidc must be set directly or via environment variable ARM_USE_OIDC. Additionally, the Azure DevOps Service Connection ID must be provided. If you are running in Azure DevOps, make sure you have serviceConnection configured for the pipeline task.",
		))
		return diags
	}
	if os.Getenv("SYSTEM_OIDCREQUESTURI") == "" {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Invalid Azure DevOps Auth",
			"ADO System OIDC Request URI is missing. This should be set by the Azure DevOps pipeline service via the SYSTEM_OIDCREQUESTURI environment variable.",
		))
	}
	if config.OIDCRequestToken == "" {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Invalid Azure DevOps Auth",
			"An access token for fetching a federation token must be provided.",
		))
	}
	if config.TenantID == "" {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Invalid Azure DevOps Auth",
			"Tenant ID is missing.",
		))
	}
	_, err := cred.Construct(ctx, config)
	if err != nil {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Invalid Azure DevOps Auth",
			fmt.Sprintf("Tried to create the credential, but received this error instead: %s.", tfdiags.FormatError(err)),
		))
	}
	return diags
}

func (cred *adoAuth) AugmentConfig(_ context.Context, config *Config) error {
	return checkNamesForAccessKeyCredentials(config.StorageAddresses)
}
