package config

import (
	"errors"
	"fmt"
	"net/url"
	"os"
	"runtime"
	"strconv"
	"strings"
)

type Environment int

const (
	EnvDevelopment Environment = iota
	EnvProduction
)

type FileStore int

const (
	FileSystemStore FileStore = iota
	S3Store
)

const DefaultBindAddr = "localhost:8888"

type Config struct {
	Environment          Environment
	BindAddr             string
	TLSCertFile          string
	TLSKeyFile           string
	DatabaseURL          string
	FileStore            FileStore
	FileStoreHTTPRoot    string
	FileStoreHTTPBaseURL *url.URL
	AWSAccessKeyID       string
	AWSSecretAccessKey   string
	AWSRegion            string
	S3Bucket             string
	AssetsHTTPRoot       string
	FFmpegWorkerPoolSize int
	CORSAllowedOrigins   []string
}

const Prefix = "CLIPPER_"

func envPrefix(k string) string    { return Prefix + k }
func getenvPrefix(k string) string { return os.Getenv(Prefix + k) }

func NewFromEnv() (Config, error) {
	envVarName := envPrefix("ENV")
	envString := os.Getenv(envVarName)
	var env Environment
	switch envString {
	case "production":
		env = EnvProduction
	case "development", "":
		env = EnvDevelopment
	default:
		return Config{}, fmt.Errorf("invalid %s value: %s", envVarName, envString)
	}

	bindAddr := getenvPrefix("BIND_ADDR")
	if bindAddr == "" {
		bindAddr = DefaultBindAddr
	}

	tlsCertFileName := envPrefix("TLS_CERT_FILE")
	tlsKeyFileName := envPrefix("TLS_KEY_FILE")
	tlsCertFile := os.Getenv(tlsCertFileName)
	tlsKeyFile := os.Getenv(tlsKeyFileName)
	if (tlsCertFile == "" && tlsKeyFile != "") || (tlsCertFile != "" && tlsKeyFile == "") {
		return Config{}, fmt.Errorf("both %s and %s must be set", tlsCertFileName, tlsKeyFileName)
	}

	databaseURLName := envPrefix("DATABASE_URL")
	databaseURL := os.Getenv(databaseURLName)
	if databaseURL == "" {
		return Config{}, fmt.Errorf("%s not set", databaseURLName)
	}

	fileStoreName := envPrefix("FILE_STORE")
	fileStoreString := os.Getenv(fileStoreName)
	var fileStore FileStore
	switch getenvPrefix("FILE_STORE") {
	case "s3":
		fileStore = S3Store
	case "filesystem", "":
		fileStore = FileSystemStore
	default:
		return Config{}, fmt.Errorf("invalid %s value: %s", fileStoreName, fileStoreString)
	}

	fileStoreHTTPBaseURLName := envPrefix("FILE_STORE_HTTP_BASE_URL")
	fileStoreHTTPBaseURLString := os.Getenv(fileStoreHTTPBaseURLName)
	if !strings.HasSuffix(fileStoreHTTPBaseURLString, "/") {
		fileStoreHTTPBaseURLString += "/"
	}
	fileStoreHTTPBaseURL, err := url.Parse(fileStoreHTTPBaseURLString)
	if err != nil {
		return Config{}, fmt.Errorf("invalid %s: %v", fileStoreHTTPBaseURLName, fileStoreHTTPBaseURLString)
	}

	var awsAccessKeyID, awsSecretAccessKey, awsRegion, s3Bucket, fileStoreHTTPRoot string
	if fileStore == S3Store {
		awsAccessKeyID = os.Getenv("AWS_ACCESS_KEY_ID")
		if awsAccessKeyID == "" {
			return Config{}, errors.New("AWS_ACCESS_KEY_ID not set")
		}

		awsSecretAccessKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
		if awsSecretAccessKey == "" {
			return Config{}, errors.New("AWS_SECRET_ACCESS_KEY not set")
		}

		awsRegion = os.Getenv("AWS_REGION")
		if awsRegion == "" {
			return Config{}, errors.New("AWS_REGION not set")
		}

		s3Bucket = getenvPrefix("S3_BUCKET")
		if s3Bucket == "" {
			return Config{}, errors.New("S3_BUCKET not set")
		}
	} else {
		if fileStoreHTTPRoot = getenvPrefix("FILE_STORE_HTTP_ROOT"); fileStoreHTTPRoot == "" {
			return Config{}, errors.New("FILE_STORE_HTTP_ROOT not set")
		}
	}

	assetsHTTPRoot := getenvPrefix("ASSETS_HTTP_ROOT")

	ffmpegWorkerPoolSize := runtime.NumCPU()
	ffmpegWorkerPoolSizeName := envPrefix("FFMPEG_WORKER_POOL_SIZE")
	if s := os.Getenv(ffmpegWorkerPoolSizeName); s != "" {
		if n, err := strconv.Atoi(s); err != nil {
			return Config{}, fmt.Errorf("invalid %s value: %s", ffmpegWorkerPoolSizeName, s)
		} else {
			ffmpegWorkerPoolSize = n
		}
	}

	var corsAllowedOrigins []string
	corsAllowedOriginsName := envPrefix("CORS_ALLOWED_ORIGINS")
	if s := os.Getenv(corsAllowedOriginsName); s != "" {
		corsAllowedOrigins = strings.Split(s, ",")
	}

	return Config{
		Environment:          env,
		BindAddr:             bindAddr,
		TLSCertFile:          tlsCertFile,
		TLSKeyFile:           tlsKeyFile,
		DatabaseURL:          databaseURL,
		FileStore:            fileStore,
		AWSAccessKeyID:       awsAccessKeyID,
		AWSSecretAccessKey:   awsSecretAccessKey,
		AWSRegion:            awsRegion,
		S3Bucket:             s3Bucket,
		AssetsHTTPRoot:       assetsHTTPRoot,
		FileStoreHTTPRoot:    fileStoreHTTPRoot,
		FileStoreHTTPBaseURL: fileStoreHTTPBaseURL,
		FFmpegWorkerPoolSize: ffmpegWorkerPoolSize,
		CORSAllowedOrigins:   corsAllowedOrigins,
	}, nil
}
