package pkiws

import (
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"log"
	"regexp"
	"time"

	"git.paulbsd.com/paulbsd/pki/src/cert"
	"git.paulbsd.com/paulbsd/pki/src/config"
	"git.paulbsd.com/paulbsd/pki/src/pki"
)

const timeformatstring string = "2006-01-02 15:04:05"

var domainRegex, err = regexp.Compile(`^[a-z0-9\*]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$`)

// GetCertificate get certificate from database if exists, of request it from ACME
func GetCertificate(cfg *config.Config, user *pki.User, domains *[]string) (result map[string]EntryResponse, err error) {
	err = CheckDomains(domains)
	if err != nil {
		return result, err
	}
	result = make(map[string]EntryResponse)

	for _, domain := range *domains {
		entry, err := user.GetEntry(cfg, &domain)
		if err != nil {
			certs, err := user.RequestNewCert(cfg, &domain)
			if err != nil {
				log.Printf("Error fetching new certificate %s\n", err)
				return result, err
			}
			NotBefore, NotAfter, err := GetDates(certs.Certificate)
			if err != nil {
				log.Println("Error where parsing dates")
				return result, err
			}
			entry := cert.Entry{Domain: certs.Domain,
				Certificate:   string(certs.Certificate),
				PrivateKey:    string(certs.PrivateKey),
				ValidityBegin: NotBefore,
				ValidityEnd:   NotAfter,
				AuthURL:       cfg.ACME.AuthURL}
			cfg.Db.Insert(&entry)
			result[domain] = convertEntryToResponse(entry)
			return result, err
		}
		result[domain] = convertEntryToResponse(entry)
	}
	return
}

// CheckDomains check if requested domains are valid
func CheckDomains(domains *[]string) (err error) {
	for _, domain := range *domains {
		err = CheckDomain(&domain)
		if err != nil {
			return
		}
	}
	return
}

// CheckDomain check if requested domain are valid
func CheckDomain(domain *string) (err error) {
	res := domainRegex.Match([]byte(*domain))
	if !res {
		return fmt.Errorf("Domain %s has not a valid syntax %s, please verify", *domain, err)
	}
	return
}

// GetDates decodes NotBefore and NotAfter date of cert
func GetDates(cert []byte) (NotBefore time.Time, NotAfter time.Time, err error) {
	block, _ := pem.Decode(cert)
	if block.Type == "CERTIFICATE" {
		ce, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			log.Println("Error when parsing certificate")
		}
		NotBefore = ce.NotBefore
		NotAfter = ce.NotAfter
	}
	return
}

// convertEntryToResponse converts database ACME entry to JSON ACME entry
func convertEntryToResponse(in cert.Entry) (out EntryResponse) {
	out.Domains = append(out.Domains, in.Domain)
	out.Certificate = in.Certificate
	out.PrivateKey = in.PrivateKey
	out.ValidityBegin = in.ValidityBegin.Format(timeformatstring)
	out.ValidityEnd = in.ValidityEnd.Format(timeformatstring)

	return
}

// EntryRequest
type EntryRequest struct {
	Domains []string `json:"domains"`
}

// EntryResponse is the struct defining JSON response from webservice
type EntryResponse struct {
	Domains       []string `json:"domains"`
	Certificate   string   `json:"certificate"`
	PrivateKey    string   `json:"privatekey"`
	ValidityBegin string   `json:"validitybegin"`
	ValidityEnd   string   `json:"validityend"`
}
