package caddytls

import (
	"fmt"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/mholt/caddy"
)

// StorageFor gets the storage value associated with the
// caURL, which should be unique for every different
// ACME CA.
func StorageFor(caURL string) (Storage, error) {
	if caURL == "" {
		caURL = DefaultCAUrl
	}
	if caURL == "" {
		return "", fmt.Errorf("cannot create storage without CA URL")
	}
	caURL = strings.ToLower(caURL)

	// scheme required or host will be parsed as path (as of Go 1.6)
	if !strings.Contains(caURL, "://") {
		caURL = "https://" + caURL
	}

	u, err := url.Parse(caURL)
	if err != nil {
		return "", fmt.Errorf("%s: unable to parse CA URL: %v", caURL, err)
	}

	if u.Host == "" {
		return "", fmt.Errorf("%s: no host in CA URL", caURL)
	}

	return Storage(filepath.Join(storageBasePath, u.Host)), nil
}

// Storage is a root directory and facilitates
// forming file paths derived from it. It is used
// to get file paths in a consistent, cross-
// platform way for persisting ACME assets.
// on the file system.
type Storage string

// Sites gets the directory that stores site certificate and keys.
func (s Storage) Sites() string {
	return filepath.Join(string(s), "sites")
}

// Site returns the path to the folder containing assets for domain.
func (s Storage) Site(domain string) string {
	domain = strings.ToLower(domain)
	return filepath.Join(s.Sites(), domain)
}

// SiteCertFile returns the path to the certificate file for domain.
func (s Storage) SiteCertFile(domain string) string {
	domain = strings.ToLower(domain)
	return filepath.Join(s.Site(domain), domain+".crt")
}

// SiteKeyFile returns the path to domain's private key file.
func (s Storage) SiteKeyFile(domain string) string {
	domain = strings.ToLower(domain)
	return filepath.Join(s.Site(domain), domain+".key")
}

// SiteMetaFile returns the path to the domain's asset metadata file.
func (s Storage) SiteMetaFile(domain string) string {
	domain = strings.ToLower(domain)
	return filepath.Join(s.Site(domain), domain+".json")
}

// Users gets the directory that stores account folders.
func (s Storage) Users() string {
	return filepath.Join(string(s), "users")
}

// User gets the account folder for the user with email.
func (s Storage) User(email string) string {
	if email == "" {
		email = emptyEmail
	}
	email = strings.ToLower(email)
	return filepath.Join(s.Users(), email)
}

// UserRegFile gets the path to the registration file for
// the user with the given email address.
func (s Storage) UserRegFile(email string) string {
	if email == "" {
		email = emptyEmail
	}
	email = strings.ToLower(email)
	fileName := emailUsername(email)
	if fileName == "" {
		fileName = "registration"
	}
	return filepath.Join(s.User(email), fileName+".json")
}

// UserKeyFile gets the path to the private key file for
// the user with the given email address.
func (s Storage) UserKeyFile(email string) string {
	if email == "" {
		email = emptyEmail
	}
	email = strings.ToLower(email)
	fileName := emailUsername(email)
	if fileName == "" {
		fileName = "private"
	}
	return filepath.Join(s.User(email), fileName+".key")
}

// emailUsername returns the username portion of an
// email address (part before '@') or the original
// input if it can't find the "@" symbol.
func emailUsername(email string) string {
	at := strings.Index(email, "@")
	if at == -1 {
		return email
	} else if at == 0 {
		return email[1:]
	}
	return email[:at]
}

// storageBasePath is the root path in which all TLS/ACME assets are
//  stored. Do not change this value during the lifetime of the program.
var storageBasePath = filepath.Join(caddy.AssetsPath(), "acme")