package sslcerts

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"math/big"
	"net"
	"os"
	"path/filepath"
	"server/log"
	"server/settings"
	"time"
)

func generateSelfSignedCert(ips []string) ([]byte, []byte, error) {
	priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
	if err != nil {
		return nil, nil, err
	}

	notBefore := time.Now()
	notAfter := notBefore.Add(365 * 24 * time.Hour) // Valid for 1 year

	serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
	if err != nil {
		return nil, nil, err
	}

	netIps := make([]net.IP, 0)
	if len(ips) != 0 {
		for _, ip := range ips {
			netIps = append(netIps, net.ParseIP(ip))
		}
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"TorrServer"},
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		DNSNames:              []string{"localhost"},
		IPAddresses:           netIps,
	}

	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		return nil, nil, err
	}

	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})

	privBytes, err := x509.MarshalECPrivateKey(priv)
	if err != nil {
		return nil, nil, err
	}

	privPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: privBytes})

	return certPEM, privPEM, nil
}

func MakeCertKeyFiles(ips []string) (string, string) {
	certPEM, privPEM, err := generateSelfSignedCert(ips)
	if err != nil {
		log.TLogln("Error generating certificate:", err)
		os.Exit(1)
	}
	certFile, err := os.Create(filepath.Join(settings.Path, "server.pem"))
	if err != nil {
		log.TLogln("Error creating certificate file:", err)
		os.Exit(1)
	}
	defer certFile.Close()

	privFile, err := os.Create(filepath.Join(settings.Path, "server.key"))
	if err != nil {
		log.TLogln("Error creating private key file:", err)
		os.Exit(1)
	}
	defer privFile.Close()

	_, err = certFile.Write(certPEM)
	if err != nil {
		log.TLogln("Error writing certificate file:", err)
		os.Exit(1)
	}
	_, err = privFile.Write(privPEM)
	if err != nil {
		log.TLogln("Error writing private key file:", err)
		os.Exit(1)
	}
	log.TLogln("Self-signed certificate and private key generated successfully.")

	return getAbsPath(certFile.Name()), getAbsPath(privFile.Name())
}

func getAbsPath(fileName string) string {
	filePath, err := filepath.Abs(fileName)
	if err != nil {
		log.TLogln("Error getting absolute path:", err)
		os.Exit(1)
	}
	return filePath
}

func VerifyCertKeyFiles(certFile, keyFile, port string) error {
	// Load the certificate and key
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	// Check if the certificate chain is expired
	for _, cert := range cert.Certificate {
		x509Cert, err := x509.ParseCertificate(cert)
		if err != nil {
			return err
		}
		if x509Cert.NotAfter.Before(time.Now()) {
			return errors.New("certificate has expired")
		}
	}
	// Create a TLS configuration
	config := tls.Config{
		Certificates: []tls.Certificate{cert},
	}
	// Create a listener to check the certificate and key
	ln, err := tls.Listen("tcp", ":"+port, &config)
	if err != nil {
		return err
	}
	defer ln.Close()
	log.TLogln("Certificate and key are valid.")
	return nil
}
