// Copyright 2024 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package tlsutil

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"os"
)

// LoadCertificate loads a TLS certificate from the given cert and key files.
// If both are empty, it returns nil. If only one is provided, it returns an error.
func LoadCertificate(certFile, keyFile string) (*tls.Certificate, error) {
	if certFile != "" && keyFile != "" {
		cert, err := tls.LoadX509KeyPair(certFile, keyFile)
		if err != nil {
			return nil, err
		}
		return &cert, nil
	}

	if certFile != "" || keyFile != "" {
		return nil, errors.New("tls_cert_file and tls_private_key_file must be specified together")
	}

	return nil, nil
}

// LoadCertPool loads a certificate pool from the given CA cert file.
// If the file is empty, it returns nil.
func LoadCertPool(caCertFile string) (*x509.CertPool, error) {
	if caCertFile == "" {
		return nil, nil
	}

	caCertPEM, err := os.ReadFile(caCertFile)
	if err != nil {
		return nil, fmt.Errorf("read CA cert file: %v", err)
	}
	pool := x509.NewCertPool()
	if ok := pool.AppendCertsFromPEM(caCertPEM); !ok {
		return nil, fmt.Errorf("failed to parse CA cert %q", caCertFile)
	}
	return pool, nil
}

// BuildTLSConfig creates a *tls.Config based on the encryption scheme.
// For "off", it returns nil. For "mtls", a certificate is required.
func BuildTLSConfig(scheme string, skipVerify bool, cert *tls.Certificate, pool *x509.CertPool) (*tls.Config, error) {
	if scheme == "off" {
		return nil, nil
	}
	tlsConfig := &tls.Config{
		RootCAs:            pool,
		InsecureSkipVerify: skipVerify,
	}
	if scheme == "mtls" {
		if cert == nil {
			return nil, errors.New("tls_cert_file required but not supplied")
		}
		tlsConfig.Certificates = []tls.Certificate{*cert}
	}
	return tlsConfig, nil
}
