package autoradio

import (
	"crypto/rand"
	"encoding/base32"
	"fmt"
	"hash/crc32"
	"strings"
)

const (
	// All our keys are stored in etcd below this prefix. The
	// slash is used as hierarchy separator for our keyspace.
	EtcdPrefix = "autoradio/"

	// MountPrefix stores the path to the mount configuration in
	// etcd. This should never change between releases, upgrades
	// to the configuration format should be backwards-compatible.
	MountPrefix = EtcdPrefix + "icecast/mounts/"

	// Paths for the cluster runtime data. Whenever the format of
	// this data changes, the ABIVersion should be increased. A
	// rolling restart of the cluster will then seamlessly cause a
	// transition to the new consensus (the cluster will be
	// partitioned in the meantime).
	ABIVersion = "4"

	// Prefixes for the etcd-based leader elections.
	ElectionPrefix           = EtcdPrefix + "election/" + ABIVersion + "/"
	IcecastElectionPrefix    = ElectionPrefix + "icecast/"
	TranscoderElectionPrefix = ElectionPrefix + "transcode/"

	// Prefix for the streams served directly by Icecast.
	IcecastMountPrefix = "/_stream"

	// Prefixes for etcd-based service discovery.
	EndpointPrefix       = EtcdPrefix + "endpoints/" + ABIVersion + "/"
	PublicEndpointPrefix = EndpointPrefix + "frontend/public/"
	StatusEndpointPrefix = EndpointPrefix + "frontend/status/"
)

// IcecastPort is the port that the Icecast server will listen
// on. Since we fully manage the system-wide Icecast instance,
// there's not much point in making this configurable.
var IcecastPort = 8000

// MountPathToIcecastPath returns the Icecast mount path for the given
// public mount name.
func MountPathToIcecastPath(mountPath string) string {
	return IcecastMountPrefix + mountPath
}

// IcecastPathToMountPath returns the public mount name from an
// Icecast mount path. If 'path' does not start with
// IcecastMountPrefix, it is returned unchanged (though arguably this
// should be an error).
func IcecastPathToMountPath(path string) string {
	return strings.TrimPrefix(path, IcecastMountPrefix)
}

// GeneratePassword returns a new random password.
func GeneratePassword() string {
	b := make([]byte, 20)
	if _, err := rand.Read(b); err != nil {
		panic(err)
	}
	return strings.ToLower(base32.StdEncoding.EncodeToString(b))
}

// GenerateUsername returns a username somehow related to the name of
// the mount, possibly unique (but not actually guaranteed to be so).
func GenerateUsername(path string) string {
	return fmt.Sprintf("source%d", crc32.ChecksumIEEE([]byte(path)))
}