package localserver

import (
	"context"
	"crypto"
	"crypto/hmac"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"io"
	"os"
	"path/filepath"
)

const PEM_PRIVATE_KEY = "RSA PRIVATE KEY"
const PEM_PUBLIC_KEY = "RSA PUBLIC KEY"
const OPENSSH_PRIVATE_KEY = "OPENSSH PRIVATE KEY"

type EncryptionManager struct {
	PrivKey  *rsa.PrivateKey
	PeerKeys map[string]*rsa.PublicKey
}

func NewEncryptionManager() (eManager *EncryptionManager) {
	eManager = new(EncryptionManager)
	return
}

func (em *EncryptionManager) GenerateKeyPair(ctx context.Context, name string, pwd string) (privKey *rsa.PrivateKey, err error) {
	privKey, err = rsa.GenerateKey(rand.Reader, 1<<12)
	if err != nil {
		return
	}
	block := &pem.Block{
		Type:  PEM_PRIVATE_KEY,
		Bytes: x509.MarshalPKCS1PrivateKey(privKey),
	}
	if pwd != "" {
		block, err = x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(pwd), x509.PEMCipherAES256)
		if err != nil {
			return
		}
	}
	f, err := os.Create(filepath.Join(configPath, "config", name))
	if err != nil {
		return
	}
	_, err = f.Write(pem.EncodeToMemory(block))
	if err != nil {
		return
	}
	pubBlock := &pem.Block{
		Type:  PEM_PUBLIC_KEY,
		Bytes: x509.MarshalPKCS1PublicKey(&privKey.PublicKey),
	}
	if pwd != "" {
		pubBlock, err = x509.EncryptPEMBlock(rand.Reader, pubBlock.Type, pubBlock.Bytes, []byte(pwd), x509.PEMCipherAES256)
		if err != nil {
			return
		}
	}
	pubf, err := os.Create(filepath.Join(configPath, "config", fmt.Sprintf("%s.pub", name)))
	if err != nil {
		return
	}
	_, err = pubf.Write(pem.EncodeToMemory(pubBlock))
	return
}

func (em *EncryptionManager) LoadPrivKey(privKeyPath string, password string) (err error) {
	f, err := os.Open(privKeyPath)
	if err != nil {
		return
	}
	var buff []byte
	buff, err = io.ReadAll(f)
	if err != nil {
		return
	}
	privPem, _ := pem.Decode(buff)
	var privePemBytes []byte
	if privPem.Type != PEM_PRIVATE_KEY && privPem.Type != OPENSSH_PRIVATE_KEY {
		logger.Println(privPem.Type)
		err = fmt.Errorf("RSA Private key is of wrong type")
		return
	}
	if password != "" {
		privePemBytes, err = x509.DecryptPEMBlock(privPem, []byte(password))
	} else {
		privePemBytes = privPem.Bytes
	}
	var parsedKey interface{}
	if parsedKey, err = x509.ParsePKCS1PrivateKey(privePemBytes); err != nil {
		logger.Printf("error one %v\n", err)
		if parsedKey, err = x509.ParsePKCS8PrivateKey(privePemBytes); err != nil {
			logger.Printf("error on parsing %v\n", err)
			if parsedKey, err = x509.ParseECPrivateKey(privePemBytes); err != nil {
				return
			}
		}

	}
	var privKey *rsa.PrivateKey
	var ok bool
	privKey, ok = parsedKey.(*rsa.PrivateKey)
	if !ok {
		err = fmt.Errorf("Unable to parse this private key")
		return
	}
	em.PrivKey = privKey
	return
}

func (em *EncryptionManager) ParsePrivKey(privateKey string) (privKey *rsa.PrivateKey, err error) {
	key := []byte(privateKey)
	block, _ := pem.Decode(key)
	b := block.Bytes
	privKey, err = x509.ParsePKCS1PrivateKey(b)
	if err != nil {
		return
	}
	return
}

func (em *EncryptionManager) SignRequest(privKey, id string) (sig string, err error) {
	key, err := em.ParsePrivKey(privKey)
	if err != nil {
		return
	}
	msg := []byte(id)
	hashed := sha256.Sum256(msg)
	signature, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, hashed[:])
	if err != nil {
		return
	}
	sig = base64.StdEncoding.EncodeToString(signature)
	return
}

func (em *EncryptionManager) SignRequestHMAC(id string) string {
	h := hmac.New(sha256.New, []byte(NodeToken))
	h.Write([]byte(id))
	sha := h.Sum(nil)
	sig := base64.StdEncoding.EncodeToString(sha)
	return sig
}
