// Copyright 2025 OpenPubkey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

package policy

// Note: These tests were originally generated by o3-mini and then heavily modified

import (
	"strings"
	"testing"

	"github.com/openpubkey/openpubkey/verifier"
	"github.com/stretchr/testify/require"
)

// Test for ProvidersPolicyRow.GetExpirationPolicy.
func TestProvidersPolicyRow_GetExpirationPolicy(t *testing.T) {
	tests := []struct {
		input     string
		expected  verifier.ExpirationPolicy
		expectErr bool
	}{
		{"12h", verifier.ExpirationPolicies.MAX_AGE_12HOURS, false},
		{"24h", verifier.ExpirationPolicies.MAX_AGE_24HOURS, false},
		{"48h", verifier.ExpirationPolicies.MAX_AGE_48HOURS, false},
		{"1week", verifier.ExpirationPolicies.MAX_AGE_1WEEK, false},
		{"oidc", verifier.ExpirationPolicies.OIDC, false},
		{"oidc_refreshed", verifier.ExpirationPolicies.OIDC_REFRESHED, false},
		{"never", verifier.ExpirationPolicies.NEVER_EXPIRE, false},
		{"invalid", verifier.ExpirationPolicy{}, true},
	}

	for _, tc := range tests {
		row := ProvidersRow{ExpirationPolicy: tc.input}
		res, err := row.GetExpirationPolicy()
		if tc.expectErr {
			if err == nil {
				t.Errorf("expected error for input %s, got nil", tc.input)
			}
		} else {
			if err != nil {
				t.Errorf("unexpected error for input %s: %v", tc.input, err)
			}
			if res != tc.expected {
				t.Errorf("for input %s, expected %v, got %v", tc.input, tc.expected, res)
			}
		}
	}
}

// Test for ProviderPolicy.ToString.
func TestProviderPolicy_ToString(t *testing.T) {
	policy := ProviderPolicy{}
	policy.AddRow(ProvidersRow{Issuer: "issuer1", ClientID: "client1", ExpirationPolicy: "12h"})
	policy.AddRow(ProvidersRow{Issuer: "issuer2", ClientID: "client2", ExpirationPolicy: "24h"})
	policy.AddRow(ProvidersRow{Issuer: "issuer3", ClientID: "client3", ExpirationPolicy: "48h"})
	expected := "issuer1 client1 12h\nissuer2 client2 24h\nissuer3 client3 48h\n"
	require.Equal(t, expected, policy.ToString())
}

// Test ProviderPolicy.CreateVerifier with a valid Google issuer.
func TestProviderPolicy_CreateVerifier_Google(t *testing.T) {
	policy := &ProviderPolicy{}
	policy.AddRow(ProvidersRow{
		Issuer:           "https://accounts.google.com",
		ClientID:         "test-google",
		ExpirationPolicy: "12h",
	})
	ver, err := policy.CreateVerifier()
	require.NoError(t, err)
	require.NotNil(t, ver)
}

// Test ProviderPolicy.CreateVerifier with a valid Azure issuer.
func TestProviderPolicy_CreateVerifier_Azure(t *testing.T) {
	policy := &ProviderPolicy{}
	policy.AddRow(ProvidersRow{
		Issuer:           "https://login.microsoftonline.com/tenant",
		ClientID:         "test-azure",
		ExpirationPolicy: "48h",
	})
	ver, err := policy.CreateVerifier()
	require.NoError(t, err)
	require.NotNil(t, ver)
}

func TestProviderPolicy_CreateVerifier_Gitlab(t *testing.T) {
	policy := &ProviderPolicy{}
	policy.AddRow(ProvidersRow{
		Issuer:           "https://gitlab.com",
		ClientID:         "test-gitlab",
		ExpirationPolicy: "24h",
	})
	ver, err := policy.CreateVerifier()
	require.NoError(t, err)
	require.NotNil(t, ver)
}

// Test ProviderPolicy.CreateVerifier with an invalid expiration policy.
func TestProviderPolicy_CreateVerifier_InvalidExpiration(t *testing.T) {
	policy := &ProviderPolicy{}
	policy.AddRow(ProvidersRow{
		Issuer:           "https://accounts.google.com",
		ClientID:         "test-google",
		ExpirationPolicy: "invalid",
	})
	ver, err := policy.CreateVerifier()
	require.ErrorContains(t, err, "invalid expiration policy")
	require.Nil(t, ver)
}

// Test ProviderPolicy.CreateVerifier when no providers are configured.
func TestProviderPolicy_CreateVerifier_NoProviders(t *testing.T) {
	policy := &ProviderPolicy{}
	ver, err := policy.CreateVerifier()
	require.ErrorContains(t, err, "no providers configured")
	require.Nil(t, ver)
}

// Test ProvidersFileLoader.FromTable with valid and invalid rows.
func TestProvidersFileLoader_FromTable(t *testing.T) {
	// Input with two valid rows and one invalid row.
	input := []byte("https://accounts.google.com test-google 24h\n" +
		"invalid-line\n" +
		"https://login.microsoftonline.com/tenant test-azure 48h\n" +
		"https://login.microsoftonline.com/tenant-twelve test-twelve 12h\n")
	loader := ProvidersFileLoader{}
	policy := loader.FromTable(input, "dummy-path")
	require.Equal(t, 3, len(policy.rows))
	// Check the first row.
	row1 := policy.rows[0]
	if row1.Issuer != "https://accounts.google.com" || row1.ClientID != "test-google" || row1.ExpirationPolicy != "24h" {
		t.Error("first row does not match expected values")
	}
	// Check the second row.
	row2 := policy.rows[1]
	if !strings.HasPrefix(row2.Issuer, "https://login.microsoftonline.com") ||
		row2.ClientID != "test-azure" ||
		row2.ExpirationPolicy != "48h" {
		t.Error("second row does not match expected values")
	}
	// Check the third row.
	row3 := policy.rows[2]
	if !strings.HasPrefix(row3.Issuer, "https://login.microsoftonline.com") ||
		row3.ClientID != "test-twelve" ||
		row3.ExpirationPolicy != "12h" {
		t.Error("third row does not match expected values")
	}
}

// Test ProvidersFileLoader.ToTable.
func TestProvidersFileLoader_ToTable(t *testing.T) {
	policy := ProviderPolicy{}
	policy.AddRow(ProvidersRow{
		Issuer:           "issuer1",
		ClientID:         "client1",
		ExpirationPolicy: "12h",
	})
	policy.AddRow(ProvidersRow{
		Issuer:           "issuer2",
		ClientID:         "client2",
		ExpirationPolicy: "24h",
	})
	policy.AddRow(ProvidersRow{
		Issuer:           "issuer3",
		ClientID:         "client3",
		ExpirationPolicy: "48h",
	})
	loader := ProvidersFileLoader{}
	table := loader.ToTable(policy)
	rows := table.GetRows()
	require.Equal(t, 3, len(rows))
	if rows[0][0] != "issuer1" || rows[0][1] != "client1" || rows[0][2] != "12h" {
		t.Error("first row in table does not match expected values")
	}
	if rows[1][0] != "issuer2" || rows[1][1] != "client2" || rows[1][2] != "24h" {
		t.Error("second row in table does not match expected values")
	}
	if rows[2][0] != "issuer3" || rows[2][1] != "client3" || rows[2][2] != "48h" {
		t.Error("third row in table does not match expected values")
	}
}
