package p2p

import (
	"crypto/ecdsa"
	"fmt"
	"os"
	"path"

	"github.com/ledgerwatch/erigon/crypto"
)

type NodeKeyConfig struct {
}

func (config NodeKeyConfig) DefaultPath(datadir string) string {
	return path.Join(datadir, "nodekey")
}

func (config NodeKeyConfig) generateKey() (*ecdsa.PrivateKey, error) {
	key, err := crypto.GenerateKey()
	if err != nil {
		err = fmt.Errorf("failed to generate node key: %w", err)
	}
	return key, err
}

func (config NodeKeyConfig) parseHex(hex string) (*ecdsa.PrivateKey, error) {
	key, err := crypto.HexToECDSA(hex)
	if err != nil {
		err = fmt.Errorf("failed to parse node key from %s: %w", hex, err)
	}
	return key, err
}

func (config NodeKeyConfig) load(keyfile string) (*ecdsa.PrivateKey, error) {
	key, err := crypto.LoadECDSA(keyfile)
	if err != nil {
		err = fmt.Errorf("failed to load node key from %s: %w", keyfile, err)
	}
	return key, err
}

func (config NodeKeyConfig) save(keyfile string, key *ecdsa.PrivateKey) error {
	err := os.MkdirAll(path.Dir(keyfile), 0755)
	if err == nil {
		err = crypto.SaveECDSA(keyfile, key)
	}
	if err != nil {
		return fmt.Errorf("failed to save node key to %s: %w", keyfile, err)
	}
	return nil
}

func (config NodeKeyConfig) LoadOrGenerateAndSave(keyfile string) (*ecdsa.PrivateKey, error) {
	// If file exists, try to load it.
	if _, err := os.Stat(keyfile); err == nil {
		return config.load(keyfile)
	}

	// No persistent key found, generate and store a new one.
	key, err := config.generateKey()
	if err != nil {
		return nil, err
	}
	if err := config.save(keyfile, key); err != nil {
		return nil, err
	}
	return key, nil
}

func (config NodeKeyConfig) LoadOrParseOrGenerateAndSave(file, hex, datadir string) (*ecdsa.PrivateKey, error) {
	switch {
	case file != "" && hex != "":
		return nil, fmt.Errorf("P2P node key is set as both file and hex string - these options are mutually exclusive")
	case file != "":
		return config.load(file)
	case hex != "":
		return config.parseHex(hex)
	default:
		return config.LoadOrGenerateAndSave(config.DefaultPath(datadir))
	}
}