// Package cert managed die Supervision-Identity (Cert + Private-Key).
// Cert wird einmal generiert via go-ios und persistent unter
// ~/.rebreak-supervise/ abgelegt. Default-Path matched die existing
// Bootstrap-Tool-Konvention.
package cert

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"os"
	"path/filepath"

	ios "github.com/danielpaulus/go-ios/ios"
)

// Identity ist unsere komplette Supervision-Identity.
// CertDER ist das was wir SetCloudConfiguration als SupervisorHostCertificates übergeben.
type Identity struct {
	CertDER       []byte
	PrivateKeyDER []byte
}

// DefaultDir matches die Konvention aus dem alten bootstrap-tool.
func DefaultDir() string {
	home, _ := os.UserHomeDir()
	return filepath.Join(home, ".rebreak-supervise")
}

// LoadOrCreate lädt eine existierende Identity aus dem default-dir, oder
// generiert eine neue + speichert sie. Idempotent: zweiter Call returnt
// die gleiche Identity wie der erste.
func LoadOrCreate() (*Identity, error) {
	dir := DefaultDir()
	certPath := filepath.Join(dir, "supervision-cert.pem")
	keyPath := filepath.Join(dir, "supervision-key.pem")

	id, err := load(certPath, keyPath)
	if err == nil {
		return id, nil
	}
	if !os.IsNotExist(err) && !errors.Is(err, errCorruptIdentity) {
		return nil, fmt.Errorf("cert: load existing: %w", err)
	}

	// Generieren
	id, err = generate()
	if err != nil {
		return nil, fmt.Errorf("cert: generate: %w", err)
	}
	if err := id.save(certPath, keyPath); err != nil {
		return nil, fmt.Errorf("cert: save: %w", err)
	}
	return id, nil
}

// load aus PEM-Files.
func load(certPath, keyPath string) (*Identity, error) {
	certPEM, err := os.ReadFile(certPath)
	if err != nil {
		return nil, err
	}
	keyPEM, err := os.ReadFile(keyPath)
	if err != nil {
		return nil, err
	}
	certBlock, _ := pem.Decode(certPEM)
	keyBlock, _ := pem.Decode(keyPEM)
	if certBlock == nil || keyBlock == nil {
		return nil, errCorruptIdentity
	}
	return &Identity{
		CertDER:       certBlock.Bytes,
		PrivateKeyDER: keyBlock.Bytes,
	}, nil
}

// generate via go-ios's CreateDERFormattedSupervisionCert.
func generate() (*Identity, error) {
	ca, err := ios.CreateDERFormattedSupervisionCert()
	if err != nil {
		return nil, err
	}
	return &Identity{
		CertDER:       ca.CertDER,
		PrivateKeyDER: ca.PrivateKeyDER,
	}, nil
}

func (id *Identity) save(certPath, keyPath string) error {
	dir := filepath.Dir(certPath)
	if err := os.MkdirAll(dir, 0o700); err != nil {
		return err
	}
	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: id.CertDER})
	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: id.PrivateKeyDER})
	if err := os.WriteFile(certPath, certPEM, 0o600); err != nil {
		return err
	}
	if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
		return err
	}
	return nil
}

var errCorruptIdentity = errors.New("cert: identity files corrupt — delete + regenerate")

// Parse decodes the DER-encoded cert + private key for use with Escalate.
func (id *Identity) Parse() (*x509.Certificate, *rsa.PrivateKey, error) {
	cert, err := x509.ParseCertificate(id.CertDER)
	if err != nil {
		return nil, nil, fmt.Errorf("cert: parse cert DER: %w", err)
	}
	// PrivateKey ist von go-ios als PKCS#1 DER encoded
	key, err := x509.ParsePKCS1PrivateKey(id.PrivateKeyDER)
	if err != nil {
		// Fallback: PKCS#8
		k8, err2 := x509.ParsePKCS8PrivateKey(id.PrivateKeyDER)
		if err2 != nil {
			return nil, nil, fmt.Errorf("cert: parse key DER (PKCS1 + PKCS8 failed): %w / %v", err, err2)
		}
		rsaKey, ok := k8.(*rsa.PrivateKey)
		if !ok {
			return nil, nil, fmt.Errorf("cert: key is not RSA")
		}
		key = rsaKey
	}
	return cert, key, nil
}
