// Copyright 2025 Google LLC.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package idtoken

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"google.golang.org/api/internal/credentialstype"
	"google.golang.org/api/option"
)

const (
	serviceAccountJSON = `{
		"type": "service_account",
		"project_id": "my-project"
	}`
)

func TestNewTokenSource_Validation(t *testing.T) {
	tempDir := t.TempDir()
	saFile := filepath.Join(tempDir, "sa.json")
	if err := os.WriteFile(saFile, []byte(serviceAccountJSON), 0644); err != nil {
		t.Fatalf("os.WriteFile: %v", err)
	}

	ctx := context.Background()
	aud := "test-audience"

	userCreds := []byte(`{"type": "authorized_user"}`)
	externalCreds := []byte(`{"type": "external_account"}`)

	testCases := []struct {
		name           string
		opts           option.ClientOption
		wantErr        bool
		errContains    string
		errNotContains string
	}{
		{
			name:        "FileMismatch",
			opts:        option.WithAuthCredentialsFile(ExternalAccount, saFile),
			wantErr:     true,
			errContains: "credential type mismatch",
		},
		{
			name:        "JSONMismatch",
			opts:        option.WithAuthCredentialsJSON(ExternalAccount, []byte(serviceAccountJSON)),
			wantErr:     true,
			errContains: "credential type mismatch",
		},
		{
			name:           "FileCorrect",
			opts:           option.WithAuthCredentialsFile(ServiceAccount, saFile),
			wantErr:        true, // Fails later, but not with a validation error
			errNotContains: "credential type mismatch",
		},
		{
			name:        "NotAllowed",
			opts:        option.WithAuthCredentialsJSON(credentialstype.AuthorizedUser, userCreds),
			wantErr:     true,
			errContains: "credential type not allowed",
		},
		{
			name:           "Allowed",
			opts:           option.WithAuthCredentialsJSON(credentialstype.ExternalAccount, externalCreds),
			wantErr:        true, // Fails later, but not with a validation error
			errNotContains: "credential type not allowed",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			_, err := NewTokenSource(ctx, aud, tc.opts)

			if tc.wantErr {
				if err == nil {
					t.Fatal("got nil, want error")
				}
				if tc.errContains != "" && !strings.Contains(err.Error(), tc.errContains) {
					t.Errorf("got %q, want error containing %q", err, tc.errContains)
				}
				if tc.errNotContains != "" && strings.Contains(err.Error(), tc.errNotContains) {
					t.Errorf("got %q, want error NOT containing %q", err, tc.errNotContains)
				}
			} else if err != nil {
				t.Fatalf("got %v, want nil error", err)
			}
		})
	}
}
