package config

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"gopkg.in/yaml.v2"
)

type Config struct {
	AssetListPageSize   string                 `json:"TERMINAL_ASSET_LIST_PAGE_SIZE"`
	AssetListSortBy     string                 `json:"TERMINAL_ASSET_LIST_SORT_BY"`
	HeaderTitle         string                 `json:"TERMINAL_HEADER_TITLE"`
	HostKey             string                 `json:"TERMINAL_HOST_KEY" yaml:"HOST_KEY"`
	PasswordAuth        bool                   `json:"TERMINAL_PASSWORD_AUTH" yaml:"PASSWORD_AUTH"`
	PublicKeyAuth       bool                   `json:"TERMINAL_PUBLIC_KEY_AUTH" yaml:"PUBLIC_KEY_AUTH"`
	CommandStorage      map[string]interface{} `json:"TERMINAL_COMMAND_STORAGE"`
	ReplayStorage       map[string]interface{} `json:"TERMINAL_REPLAY_STORAGE" yaml:"REPLAY_STORAGE"`
	SessionKeepDuration time.Duration          `json:"TERMINAL_SESSION_KEEP_DURATION"`
	TelnetRegex         string                 `json:"TERMINAL_TELNET_REGEX"`
	MaxIdleTime         time.Duration          `json:"SECURITY_MAX_IDLE_TIME"`
	SftpRoot            string                 `json:"TERMINAL_SFTP_ROOT" yaml:"SFTP_ROOT"`
	ShowHiddenFile      bool                   `yaml:"SFTP_SHOW_HIDDEN_FILE"`
	Name                string                 `yaml:"NAME"`
	SecretKey           string                 `yaml:"SECRET_KEY"`
	HostKeyFile         string                 `yaml:"HOST_KEY_FILE"`
	CoreHost            string                 `yaml:"CORE_HOST"`
	BootstrapToken      string                 `yaml:"BOOTSTRAP_TOKEN"`
	BindHost            string                 `yaml:"BIND_HOST"`
	SSHPort             string                 `yaml:"SSHD_PORT"`
	HTTPPort            string                 `yaml:"HTTPD_PORT"`
	SSHTimeout          time.Duration          `yaml:"SSH_TIMEOUT"`
	AccessKey           string                 `yaml:"ACCESS_KEY"`
	AccessKeyFile       string                 `yaml:"ACCESS_KEY_FILE"`
	LogLevel            string                 `yaml:"LOG_LEVEL"`
	HeartbeatDuration   time.Duration          `yaml:"HEARTBEAT_INTERVAL"`
	RootPath            string                 `yaml:"ROOT_PATH"`
	Comment             string                 `yaml:"COMMENT"`
	Language            string                 `yaml:"LANG"`
	LanguageCode        string                 `yaml:"LANGUAGE_CODE"` // Abandon
	UploadFailedReplay  bool                   `yaml:"UPLOAD_FAILED_REPLAY_ON_START"`
}

func (c *Config) EnsureConfigValid() {
	// 兼容原来config
	if c.LanguageCode != "" && c.Language == "" {
		c.Language = c.LanguageCode
	}
	// 确保至少有一个认证
	if !c.PublicKeyAuth && !c.PasswordAuth {
		c.PasswordAuth = true
	}
}

func (c *Config) LoadFromYAML(body []byte) error {
	err := yaml.Unmarshal(body, c)
	if err != nil {
		log.Printf("Load yaml error: %v", err)
	}
	return err
}

func (c *Config) LoadFromYAMLPath(filepath string) error {
	body, err := ioutil.ReadFile(filepath)
	if err != nil {
		log.Printf("Not found file: %s", filepath)
	}
	return c.LoadFromYAML(body)
}

func (c *Config) LoadFromJSON(body []byte) error {
	err := json.Unmarshal(body, c)
	if err != nil {
		log.Printf("Config load yaml error")
	}
	return nil
}

func (c *Config) LoadFromEnv() error {
	envMap := make(map[string]string)
	env := os.Environ()
	for _, v := range env {
		vSlice := strings.Split(v, "=")
		key := vSlice[0]
		value := vSlice[1]
		envMap[key] = value
	}
	envYAML, err := yaml.Marshal(&envMap)
	if err != nil {
		log.Fatalf("Error occur: %v", err)
	}
	return c.LoadFromYAML(envYAML)
}

func (c *Config) Load(filepath string) error {
	err := c.LoadFromYAMLPath(filepath)
	if err != nil {
		return err
	}
	err = c.LoadFromEnv()
	return err
}

var lock = new(sync.RWMutex)
var name, _ = os.Hostname()
var rootPath, _ = os.Getwd()
var Conf = &Config{
	Name:               name,
	CoreHost:           "http://localhost:8080",
	BootstrapToken:     "",
	BindHost:           "0.0.0.0",
	SSHPort:            "2222",
	SSHTimeout:         15,
	HTTPPort:           "5000",
	HeartbeatDuration:  10,
	AccessKey:          "",
	AccessKeyFile:      "data/keys/.access_key",
	LogLevel:           "DEBUG",
	HostKeyFile:        "data/keys/host_key",
	HostKey:            "",
	RootPath:           rootPath,
	Comment:            "Coco",
	Language:           "zh",
	ReplayStorage:      map[string]interface{}{"TYPE": "server"},
	CommandStorage:     map[string]interface{}{"TYPE": "server"},
	UploadFailedReplay: true,
	SftpRoot:           "/tmp",
	ShowHiddenFile:     false,
}

func SetConf(conf *Config) {
	lock.Lock()
	defer lock.Unlock()
	Conf = conf
}

func GetConf() *Config {
	lock.RLock()
	defer lock.RUnlock()
	var conf Config
	if confBytes, err := json.Marshal(Conf); err == nil {
		_ = json.Unmarshal(confBytes, &conf)
	}
	return &conf
}
