package acme

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"io/ioutil"

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

type AcmeAccount struct {
	Email        string
	Registration *registration.Resource
	Key          crypto.PrivateKey
}

func (a *AcmeAccount) GetEmail() string {
	return a.Email
}

func (a *AcmeAccount) GetPrivateKey() crypto.PrivateKey {
	return a.Key
}

func (a *AcmeAccount) GetRegistration() *registration.Resource {
	return a.Registration
}

func (a *AcmeAccount) FlushToDisk(storage string) {
	data := map[string]interface{}{
		"email": a.Email,
		"reg":   a.Registration,
		"private_key_encoded": base64.StdEncoding.EncodeToString(
			certcrypto.PEMEncode(a.Key),
		),
	}
	raw, _ := json.Marshal(data)
	ioutil.WriteFile(storage, raw, 0600)
}

func ClientFromFile(storage, acmeServer string) (*lego.Client, error) {
	file, err := ioutil.ReadFile(storage)
	if err != nil {
		return nil, err
	}

	var data map[string]interface{}
	_ = json.Unmarshal(file, &data)
	reg, _ := data["reg"].(map[string]interface{})
	accountRaw := reg["body"].(map[string]interface{})
	contact := accountRaw["contact"].([]interface{})
	contacts := make([]string, 0)
	for _, v := range contact {
		contacts = append(contacts, v.(string))
	}

	registration := registration.Resource{
		URI: reg["uri"].(string),
		Body: acme.Account{
			Status:               accountRaw["status"].(string),
			Contact:              contacts,
			TermsOfServiceAgreed: true,
			//Orders: accountRaw["orders"].(string),
		},
	}

	pkEncoded, err := base64.StdEncoding.DecodeString(data["private_key_encoded"].(string))
	pk, err := certcrypto.ParsePEMPrivateKey(pkEncoded)
	account := AcmeAccount{
		Email:        data["email"].(string),
		Key:          pk.(*ecdsa.PrivateKey),
		Registration: &registration,
	}
	config := lego.NewConfig(&account)
	config.CADirURL = acmeServer
	config.Certificate.KeyType = certcrypto.RSA2048

	client, err := lego.NewClient(config)
	if err != nil {
		return nil, err
	}

	return client, nil
}

func GenerateNewAccount(email, storage, acmeServer string) (*lego.Client, error) {
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return nil, err
	}

	account := AcmeAccount{
		Email: email,
		Key:   privateKey,
	}
	config := lego.NewConfig(&account)
	config.CADirURL = acmeServer
	config.Certificate.KeyType = certcrypto.RSA2048

	client, err := lego.NewClient(config)
	if err != nil {
		return nil, err
	}

	// Register it
	req, err := client.Registration.Register(
		registration.RegisterOptions{
			TermsOfServiceAgreed: true,
		},
	)
	if err != nil {
		return nil, err
	}
	account.Registration = req
	account.FlushToDisk(storage)

	return client, nil
}
