package certificates

import (
	"crypto/rsa"
	"crypto/tls"
	"encoding/base64"
	"encoding/pem"
	"time"

	"bytes"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"math/big"

	"github.com/go-acme/lego/v4/certcrypto"
	"github.com/go-acme/lego/v4/certificate"
	"github.com/go-acme/lego/v4/lego"
)

func ObtainNewCertificate(domains []string, acmeClient *lego.Client) (CertificateWrapper, error) {
	req := certificate.ObtainRequest{
		Domains: domains,
		Bundle:  true,
	}
	cert, err := acmeClient.Certificate.Obtain(req)
	if err != nil {
		return CertificateWrapper{}, err
	}

	tlsCert, err := tls.X509KeyPair(cert.Certificate, cert.PrivateKey)
	if err != nil {
		return CertificateWrapper{}, err
	}

	wrapper := CertificateWrapper{
		TlsCertificate: &tlsCert,
		Domain:         cert.Domain,
		//NotAfter: tlsCert.Leaf.NotAfter,
		NotAfter:          time.Now().Add(time.Hour * 24 * 60),
		PrivateKeyEncoded: base64.StdEncoding.EncodeToString(cert.PrivateKey),
		Certificate:       cert.Certificate,
		IssuerCertificate: cert.IssuerCertificate,
		CertificateUrl:    cert.CertURL,
	}
	return wrapper, nil
}

// Generate a fallback certificate for the domain.
func MakeFallbackCertificate(pagesDomain string) (*CertificateWrapper, error) {
	key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048)
	if err != nil {
		return nil, err
	}

	notAfter := time.Now().Add(time.Hour * 24 * 7)
	cert := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName:   pagesDomain,
			Organization: []string{"Pages Server"},
		},
		NotAfter:              notAfter,
		NotBefore:             time.Now(),
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}
	certBytes, err := x509.CreateCertificate(
		rand.Reader,
		&cert,
		&cert,
		&key.(*rsa.PrivateKey).PublicKey,
		key,
	)
	if err != nil {
		return nil, err
	}

	out := &bytes.Buffer{}
	err = pem.Encode(out, &pem.Block{
		Bytes: certBytes,
		Type:  "CERTIFICATE",
	})
	if err != nil {
		return nil, err
	}

	outBytes := out.Bytes()
	res := &certificate.Resource{
		PrivateKey:        certcrypto.PEMEncode(key),
		Certificate:       outBytes,
		IssuerCertificate: outBytes,
		Domain:            pagesDomain,
	}
	tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey)
	if err != nil {
		return nil, err
	}
	return &CertificateWrapper{
		TlsCertificate:    &tlsCertificate,
		Domain:            pagesDomain,
		NotAfter:          notAfter,
		PrivateKeyEncoded: base64.StdEncoding.EncodeToString(certcrypto.PEMEncode(key)),
		Certificate:       outBytes,
		IssuerCertificate: outBytes,
		CertificateUrl:    "localhost",
	}, nil
}
