package main

import (
	"crypto/ed25519"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"os"
	"path/filepath"
	"time"

	"github.com/cloudflare/cfssl/config"
	"github.com/cloudflare/cfssl/helpers/derhelpers"
	"github.com/cloudflare/cfssl/log"
)

type SecretData struct {
	clusters map[string]*ClusterSecrets
	config   *config.Config
}

type ClusterSecrets struct {
	CAs         map[string]*CA
	Tokens      map[string]string
	Passwords   map[string]string
	SSHKeyPairs map[string][]SSHKeyPair
}

type KeyCert struct {
	Key     []byte
	Cert    []byte
	ReqHash string
}

func secretDataPath() string {
	return filepath.Join(*dataDir, "secret-data.json")
}

func loadSecretData(config *config.Config) (sd *SecretData, err error) {
	log.Info("Loading secret data")

	sd = &SecretData{
		clusters: make(map[string]*ClusterSecrets),
		config:   config,
	}

	ba, err := os.ReadFile(secretDataPath())
	if err != nil {
		if os.IsNotExist(err) {
			err = nil
			return
		}
		return
	}

	if err = json.Unmarshal(ba, &sd.clusters); err != nil {
		return
	}

	return
}

func checkCertUsable(certPEM []byte) error {
	block, _ := pem.Decode(certPEM)
	if block == nil {
		return errors.New("no PEM block found")
	}
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return err
	}

	certDuration := cert.NotAfter.Sub(cert.NotBefore)
	delayBeforeRegen := certDuration / 3 // TODO allow configuration

	if cert.NotAfter.Sub(time.Now()) < delayBeforeRegen {
		return errors.New("too old")
	}

	return nil
}

func dlsSigningKeys() (ed25519.PrivateKey, ed25519.PublicKey) {
	var signerDER []byte

	if err := readSecret("signer", &signerDER); os.IsNotExist(err) {
		_, key, err := ed25519.GenerateKey(nil)
		if err != nil {
			panic(err)
		}

		signerDER, err = derhelpers.MarshalEd25519PrivateKey(key)
		if err != nil {
			panic(err)
		}

		writeSecret("signer", signerDER)
	} else if err != nil {
		panic(err)
	}

	pkeyGeneric, err := derhelpers.ParseEd25519PrivateKey(signerDER)
	if err != nil {
		panic(err)
	}

	pkey := pkeyGeneric.(ed25519.PrivateKey)
	pubkey := pkey.Public().(ed25519.PublicKey)

	return pkey, pubkey
}
