package pki

import (
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"fmt"
	"math"
	"math/big"
	"os"
	"os/exec"
	"path"
	"time"
)

func ClientCertificatePath(serial uint64) string {
	return path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), "cert.pem")
}

func ClientPrivkeyPath(serial uint64) string {
	return path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), "privkey.pem")
}

func ClientP12Path(serial uint64) string {
	return path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), "team.p12")
}

func GenerateClient(serial uint64, notBefore time.Time, notAfter time.Time, parent_cert *x509.Certificate, parent_priv *ecdsa.PrivateKey) error {
	var certid big.Int
	certid.SetUint64(serial)
	client := &x509.Certificate{
		SerialNumber: &certid,
		Subject: pkix.Name{
			Organization:       []string{"EPITA"},
			OrganizationalUnit: []string{"SRS laboratory"},
			Country:            []string{"FR"},
			Locality:           []string{"Paris"},
			CommonName:         fmt.Sprintf("TEAM-%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)),
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		IsCA:                  false,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
		KeyUsage:              x509.KeyUsageDigitalSignature,
		BasicConstraintsValid: true,
	}

	pub, priv, err := GeneratePrivKey()
	if err != nil {
		return err
	}

	client_b, err := x509.CreateCertificate(rand.Reader, client, parent_cert, pub, parent_priv)
	if err != nil {
		return err
	}

	// Create intermediate directory
	os.MkdirAll(path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2))), 0777)

	// Save certificate to file
	if err := saveCertificate(ClientCertificatePath(serial), client_b); err != nil {
		return err
	}

	// Save private key to file
	if err := savePrivateKey(ClientPrivkeyPath(serial), priv); err != nil {
		return err
	}

	return nil
}

func WriteP12(serial uint64, password string) error {
	cmd := exec.Command("/usr/bin/openssl", "pkcs12", "-export",
		"-inkey", ClientPrivkeyPath(serial),
		"-in", ClientCertificatePath(serial),
		"-name", fmt.Sprintf("TEAM-%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)),
		"-passout", "pass:" + password,
		"-out", ClientP12Path(serial))

	return cmd.Run()
}
