// Copyright IBM Corp. 2016, 2025
// SPDX-License-Identifier: BUSL-1.1

package pki

import (
	"context"
	"testing"

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

// TestManualChainValidation creates a series of issuers, and then tries to set the manual_chain on the final issuer
// to something completely incorrect (missing an intermediate issuer).  In this case, the attempt to write the manual
// chain throws an error, and the manual chain is not updated.
func TestManualChainValidation(t *testing.T) {
	// Set Up a Cluster
	cluster, client := setupTestPkiCluster(t)
	defer cluster.Cleanup()

	// Set Up Root-A
	mount := "pki"

	resp, err := client.Logical().WriteWithContext(context.Background(), mount+"/issuers/generate/root/internal",
		map[string]interface{}{
			"issuer_name": "rootCa",
			"key_name":    "root-key",
			"key_type":    "ec",
			"common_name": "Test Root",
			"ttl":         "7200h",
		})
	require.NoError(t, err, "failed creating root CA")

	// Set Up Int-A
	resp, err = client.Logical().WriteWithContext(context.Background(), mount+"/issuers/generate/intermediate/internal",
		map[string]interface{}{
			"key_name":    "int-a-key",
			"key_type":    "ec",
			"common_name": "Test Int A",
		})
	require.NoError(t, err, "failed creating intermediary CSR")
	intermediateCSR := resp.Data["csr"].(string)

	// Sign the intermediate CSR
	resp, err = client.Logical().Write(mount+"/issuer/rootCa/sign-intermediate", map[string]interface{}{
		"csr":         intermediateCSR,
		"ttl":         "7100h",
		"common_name": "Test Int A",
	})
	require.NoError(t, err, "failed signing intermediary CSR")
	intermediateCertPEM := resp.Data["certificate"].(string)

	// Import the intermediate cert
	resp, err = client.Logical().Write(mount+"/issuers/import/cert", map[string]interface{}{
		"pem_bundle": intermediateCertPEM,
	})
	require.NoError(t, err, "failed importing intermediary cert")
	importedIssuersRaw := resp.Data["imported_issuers"].([]interface{})
	require.Len(t, importedIssuersRaw, 1)
	intCaUuid := importedIssuersRaw[0].(string)

	_, err = client.Logical().Write(mount+"/issuer/"+intCaUuid, map[string]interface{}{
		"issuer_name": "intA",
	})
	require.NoError(t, err, "failed updating issuer name")

	// Set Up Int-B (Child of Int-A)
	resp, err = client.Logical().WriteWithContext(context.Background(), mount+"/issuers/generate/intermediate/internal",
		map[string]interface{}{
			"key_name":    "int-b-key",
			"key_type":    "ec",
			"common_name": "Test Int B",
		})
	require.NoError(t, err, "failed creating intermediary CSR")
	subIntermediateCSR := resp.Data["csr"].(string)

	// Sign the intermediate CSR
	resp, err = client.Logical().Write(mount+"/issuer/intA/sign-intermediate", map[string]interface{}{
		"csr":         subIntermediateCSR,
		"ttl":         "7100h",
		"common_name": "Test Int B",
	})
	require.NoError(t, err, "failed signing intermediary CSR")
	subIntermediateCertPEM := resp.Data["certificate"].(string)

	// Import the intermediate cert
	resp, err = client.Logical().Write(mount+"/issuers/import/cert", map[string]interface{}{
		"pem_bundle": subIntermediateCertPEM,
	})
	require.NoError(t, err, "failed importing intermediary cert")
	subImportedIssuersRaw := resp.Data["imported_issuers"].([]interface{})
	require.Len(t, subImportedIssuersRaw, 1)
	subIntCaUuid := subImportedIssuersRaw[0].(string)

	resp, err = client.Logical().Write(mount+"/issuer/"+subIntCaUuid, map[string]interface{}{
		"issuer_name": "intB",
	})
	require.NoError(t, err, "failed updating issuer name")

	// Try to Set Int-B Manual Chain to Just Be Root-A; Expect An Error
	resp, err = client.Logical().Write(mount+"/issuer/intB", map[string]interface{}{
		"issuer_name":  "intB",
		"manual_chain": []string{"intB", "rootCa"}, // Misses "intA" which issued "intB"
	})
	require.Error(t, err, "failed updating intermediary cert")

	resp, err = client.Logical().Read(mount + "/issuer/intB")
	require.NoError(t, err, "failed reading intermediary cert")
	require.Nil(t, resp.Data["manual_chain"], "error reverting manual chain, got non-nil manual chain")
}
