package caddytls

import (
	"errors"
	"net/url"
)

// ErrStorageNotFound is returned by Storage implementations when data is
// expected to be present but is not.
var ErrStorageNotFound = errors.New("data not found")

// StorageCreator is a function type that is used in the Config to instantiate
// a new Storage instance. This function can return a nil Storage even without
// an error.
type StorageCreator func(caURL *url.URL) (Storage, error)

// SiteData contains persisted items pertaining to an individual site.
type SiteData struct {
	// Cert is the public cert byte array.
	Cert []byte
	// Key is the private key byte array.
	Key []byte
	// Meta is metadata about the site used by Caddy.
	Meta []byte
}

// UserData contains persisted items pertaining to a user.
type UserData struct {
	// Reg is the user registration byte array.
	Reg []byte
	// Key is the user key byte array.
	Key []byte
}

// Storage is an interface abstracting all storage used by Caddy's TLS
// subsystem. Implementations of this interface store both site and
// user data.
type Storage interface {
	// SiteExists returns true if this site exists in storage.
	// Site data is considered present when StoreSite has been called
	// successfully (without DeleteSite having been called, of course).
	SiteExists(domain string) bool

	// LoadSite obtains the site data from storage for the given domain and
	// returns it. If data for the domain does not exist, the
	// ErrStorageNotFound error instance is returned. For multi-server
	// storage, care should be taken to make this load atomic to prevent
	// race conditions that happen with multiple data loads.
	LoadSite(domain string) (*SiteData, error)

	// StoreSite persists the given site data for the given domain in
	// storage. For multi-server storage, care should be taken to make this
	// call atomic to prevent half-written data on failure of an internal
	// intermediate storage step. Implementers can trust that at runtime
	// this function will only be invoked after LockRegister and before
	// UnlockRegister of the same domain.
	StoreSite(domain string, data *SiteData) error

	// DeleteSite deletes the site for the given domain from storage.
	// Multi-server implementations should attempt to make this atomic. If
	// the site does not exist, the ErrStorageNotFound error instance is
	// returned.
	DeleteSite(domain string) error

	// LockRegister is called before Caddy attempts to obtain or renew a
	// certificate. This function is used as a mutex/semaphore for making
	// sure something else isn't already attempting obtain/renew. It should
	// return true (without error) if the lock is successfully obtained
	// meaning nothing else is attempting renewal. It should return false
	// (without error) if this domain is already locked by something else
	// attempting renewal. As a general rule, if this isn't multi-server
	// shared storage, this should always return true. To prevent deadlocks
	// for multi-server storage, all internal implementations should put a
	// reasonable expiration on this lock in case UnlockRegister is unable to
	// be called due to system crash. Errors should only be returned in
	// exceptional cases. Any error will prevent renewal.
	LockRegister(domain string) (bool, error)

	// UnlockRegister is called after Caddy has attempted to obtain or renew
	// a certificate, regardless of whether it was successful. If
	// LockRegister essentially just returns true because this is not
	// multi-server storage, this can be a no-op. Otherwise this should
	// attempt to unlock the lock obtained in this process by LockRegister.
	// If no lock exists, the implementation should not return an error. An
	// error is only for exceptional cases.
	UnlockRegister(domain string) error

	// LoadUser obtains user data from storage for the given email and
	// returns it. If data for the email does not exist, the
	// ErrStorageNotFound error instance is returned. Multi-server
	// implementations should take care to make this operation atomic for
	// all loaded data items.
	LoadUser(email string) (*UserData, error)

	// StoreUser persists the given user data for the given email in
	// storage. Multi-server implementations should take care to make this
	// operation atomic for all stored data items.
	StoreUser(email string, data *UserData) error

	// MostRecentUserEmail provides the most recently used email parameter
	// in StoreUser. The result is an empty string if there are no
	// persisted users in storage.
	MostRecentUserEmail() string
}