package service

import (
	"github.com/leanote/leanote/app/info"
	. "github.com/leanote/leanote/app/lea"
	"github.com/leanote/leanote/app/db"
	"gopkg.in/mgo.v2/bson"
	"github.com/revel/revel"
	"time"
	"os"
	"os/exec"
	"fmt"
	"strings"
	"strconv"
)

// 配置服务
// 只是全局的, 用户的配置没有
type ConfigService struct {
	adminUserId string
	siteUrl string
	adminUsername string
	// 全局的
	GlobalAllConfigs map[string]interface{}
	GlobalStringConfigs map[string]string
	GlobalArrayConfigs map[string][]string
	GlobalMapConfigs map[string]map[string]string
	GlobalArrMapConfigs map[string][]map[string]string
}
// appStart时 将全局的配置从数据库中得到作为全局
func (this *ConfigService) InitGlobalConfigs() bool {
	this.GlobalAllConfigs = map[string]interface{}{}
	this.GlobalStringConfigs = map[string]string{}
	this.GlobalArrayConfigs = map[string][]string{}
	this.GlobalMapConfigs = map[string]map[string]string{}
	this.GlobalArrMapConfigs = map[string][]map[string]string{}
	
	this.adminUsername, _ = revel.Config.String("adminUsername")
	if this.adminUsername == "" {
		this.adminUsername = "admin"
	}
	this.siteUrl, _ = revel.Config.String("site.url")
	
	userInfo := userService.GetUserInfoByAny(this.adminUsername)
	if userInfo.UserId == "" {
		return false
	}
	this.adminUserId = userInfo.UserId.Hex()
	
	configs := []info.Config{}
	db.ListByQ(db.Configs, bson.M{"UserId": userInfo.UserId}, &configs)
	
	for _, config := range configs {
		if config.IsArr {
			this.GlobalArrayConfigs[config.Key] = config.ValueArr
			this.GlobalAllConfigs[config.Key] = config.ValueArr
		} else if config.IsMap {
			this.GlobalMapConfigs[config.Key] = config.ValueMap
			this.GlobalAllConfigs[config.Key] = config.ValueMap
		} else if config.IsArrMap {
			this.GlobalArrMapConfigs[config.Key] = config.ValueArrMap
			this.GlobalAllConfigs[config.Key] = config.ValueArrMap
		} else {
			this.GlobalStringConfigs[config.Key] = config.ValueStr
			this.GlobalAllConfigs[config.Key] = config.ValueStr
		}
	}
	
	return true
}

func (this *ConfigService) GetSiteUrl() string {
	return this.siteUrl;
}
func (this *ConfigService) GetAdminUsername() string {
	return this.adminUsername
}
func (this *ConfigService) GetAdminUserId() string {
	return this.adminUserId
}

// 通用方法
func (this *ConfigService) updateGlobalConfig(userId, key string, value interface{}, isArr, isMap, isArrMap bool) bool {
	// 判断是否存在
	if _, ok := this.GlobalAllConfigs[key]; !ok {
		// 需要添加
		config := info.Config{ConfigId: bson.NewObjectId(), 
			UserId: bson.ObjectIdHex(userId),
			Key: key,
			IsArr: isArr,
			IsMap: isMap,
			IsArrMap: isArrMap,
			UpdatedTime: time.Now(),
		}
		if(isArr) {
			v, _ := value.([]string)
			config.ValueArr = v
			this.GlobalArrayConfigs[key] = v
		} else if isMap {
			v, _ := value.(map[string]string)
			config.ValueMap = v
			this.GlobalMapConfigs[key] = v
		} else if isArrMap {
			v, _ := value.([]map[string]string)
			config.ValueArrMap = v
			this.GlobalArrMapConfigs[key] = v
		} else {
			v, _ := value.(string)
			config.ValueStr = v
			this.GlobalStringConfigs[key] = v
		}
		return db.Insert(db.Configs, config)
	} else {
		i := bson.M{"UpdatedTime": time.Now()}
		this.GlobalAllConfigs[key] = value
		if(isArr) {
			v, _ := value.([]string)
			i["ValueArr"] = v
			this.GlobalArrayConfigs[key] = v
		} else if isMap {
			v, _ := value.(map[string]string)
			i["ValueMap"] = v
			this.GlobalMapConfigs[key] = v
		} else if isArrMap {
			v, _ := value.([]map[string]string)
			i["ValueArrMap"] = v
			this.GlobalArrMapConfigs[key] = v
		} else {
			v, _ := value.(string)
			i["ValueStr"] = v
			this.GlobalStringConfigs[key] = v
		}
		return db.UpdateByQMap(db.Configs, bson.M{"UserId": bson.ObjectIdHex(userId), "Key": key}, i)
	}
}

// 更新用户配置
func (this *ConfigService) UpdateGlobalStringConfig(userId, key string, value string) bool {
	return this.updateGlobalConfig(userId, key, value, false, false, false)
}
func (this *ConfigService) UpdateGlobalArrayConfig(userId, key string, value []string) bool {
	return this.updateGlobalConfig(userId, key, value, true, false, false)
}
func (this *ConfigService) UpdateGlobalMapConfig(userId, key string, value map[string]string) bool {
	return this.updateGlobalConfig(userId, key, value, false, true, false)
}
func (this *ConfigService) UpdateGlobalArrMapConfig(userId, key string, value []map[string]string) bool {
	return this.updateGlobalConfig(userId, key, value, false, false, true)
}

// 获取全局配置, 博客平台使用
func (this *ConfigService) GetGlobalStringConfig(key string) string {
	return this.GlobalStringConfigs[key]
}
func (this *ConfigService) GetGlobalArrayConfig(key string) []string {
	arr := this.GlobalArrayConfigs[key]
	if arr == nil {
		return []string{}
	}
	return arr
}
func (this *ConfigService) GetGlobalMapConfig(key string) map[string]string {
	m := this.GlobalMapConfigs[key]
	if m == nil {
		return map[string]string{}
	}
	return m
}
func (this *ConfigService) GetGlobalArrMapConfig(key string) []map[string]string {
	m := this.GlobalArrMapConfigs[key]
	if m == nil {
		return []map[string]string{}
	}
	return m
}

func (this *ConfigService) IsOpenRegister() bool {
	return this.GetGlobalStringConfig("openRegister") != ""
}
//-------
// 修改共享笔记的配置
func (this *ConfigService) UpdateShareNoteConfig(registerSharedUserId string, 
	registerSharedNotebookPerms, registerSharedNotePerms []int, 
	registerSharedNotebookIds, registerSharedNoteIds, registerCopyNoteIds []string) (ok bool, msg string) {
	
	defer func() { 
		if err := recover(); err != nil {
			ok = false
			msg = fmt.Sprint(err)
		}
	}();
	
	// 用户是否存在?
	if registerSharedUserId == "" {
		ok = true
		msg = "share userId is blank, So it share nothing to register"
		this.UpdateGlobalStringConfig(this.adminUserId, "registerSharedUserId", "")
		return 
	} else {
		user := userService.GetUserInfo(registerSharedUserId)
		if user.UserId == "" {
			ok = false
			msg = "no such user: " + registerSharedUserId
			return
		} else {
			this.UpdateGlobalStringConfig(this.adminUserId, "registerSharedUserId", registerSharedUserId)
		}
	}
	
	notebooks := []map[string]string{}
	// 共享笔记本
	if len(registerSharedNotebookIds) > 0 {
		for i := 0; i < len(registerSharedNotebookIds); i++ {
			// 判断笔记本是否存在
			notebookId := registerSharedNotebookIds[i]
			if notebookId == "" {
				continue
			}
			notebook := notebookService.GetNotebook(notebookId, registerSharedUserId)
			if notebook.NotebookId == "" {
				ok = false
				msg = "The user has no such notebook: " + notebookId
				return
			} else {
				perm := "0";
				if registerSharedNotebookPerms[i] == 1 {
					perm = "1"
				}
				notebooks = append(notebooks, map[string]string{"notebookId": notebookId, "perm": perm})
			}
		}
	}
	this.UpdateGlobalArrMapConfig(this.adminUserId, "registerSharedNotebooks", notebooks)
	
	notes := []map[string]string{}
	// 共享笔记
	if len(registerSharedNoteIds) > 0 {
		for i := 0; i < len(registerSharedNoteIds); i++ {
			// 判断笔记本是否存在
			noteId := registerSharedNoteIds[i]
			if noteId == "" {
				continue
			}
			note := noteService.GetNote(noteId, registerSharedUserId)
			if note.NoteId == "" {
				ok = false
				msg = "The user has no such note: " + noteId
				return
			} else {
				perm := "0";
				if registerSharedNotePerms[i] == 1 {
					perm = "1"
				}
				notes = append(notes, map[string]string{"noteId": noteId, "perm": perm})
			}
		}
	}
	this.UpdateGlobalArrMapConfig(this.adminUserId, "registerSharedNotes", notes)
	
	// 复制
	noteIds := []string{}
	if len(registerCopyNoteIds) > 0 {
		for i := 0; i < len(registerCopyNoteIds); i++ {
			// 判断笔记本是否存在
			noteId := registerCopyNoteIds[i]
			if noteId == "" {
				continue
			}
			note := noteService.GetNote(noteId, registerSharedUserId)
			if note.NoteId == "" {
				ok = false
				msg = "The user has no such note: " + noteId
				return
			} else {
				noteIds = append(noteIds, noteId)
			}
		}
	}
	this.UpdateGlobalArrayConfig(this.adminUserId, "registerCopyNoteIds", noteIds)
	
	ok = true
	return
}

// 添加备份
func (this *ConfigService) AddBackup(path, remark string) bool {
	backups := this.GetGlobalArrMapConfig("backups") // [{}, {}]
	n := time.Now().Unix()
	nstr := fmt.Sprintf("%v", n)
	backups = append(backups, map[string]string{"createdTime": nstr, "path": path, "remark": remark})
	return this.UpdateGlobalArrMapConfig(this.adminUserId, "backups", backups)
}

func (this *ConfigService) getBackupDirname() string {
	n := time.Now()
	y, m, d := n.Date()
	return strconv.Itoa(y) + "_" + m.String() + "_" + strconv.Itoa(d) + "_" + fmt.Sprintf("%v", n.Unix())
}
func (this *ConfigService) Backup(remark string) (ok bool, msg string) {
	binPath := configService.GetGlobalStringConfig("mongodumpPath")
	config := revel.Config;
	dbname, _ := config.String("db.dbname")
	host, _ := revel.Config.String("db.host")
	port, _ := revel.Config.String("db.port")
	username, _ := revel.Config.String("db.username")
	password, _ := revel.Config.String("db.password")
	// mongodump -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM
	binPath = binPath + " -h " + host + " -d " + dbname + " -port " + port
	if username != "" {
		binPath += " -u " + username + " -p " + password
	}
	// 保存的路径
	dir := revel.BasePath + "/mongodb_backup/" + this.getBackupDirname()
	binPath += " -o " + dir
	err := os.MkdirAll(dir, 0755)
	if err != nil {
		ok = false
		msg = fmt.Sprintf("%v", err)
		return
	}
	
	cmd := exec.Command("/bin/sh", "-c", binPath)
	Log(binPath);
	b, err := cmd.Output()
    if err != nil {
    	msg = fmt.Sprintf("%v", err)
		ok = false
    	Log("error:......")
    	Log(string(b))
    	return
    }
	ok = configService.AddBackup(dir, remark)
    return ok, msg
}
// 还原
func (this *ConfigService) Restore(createdTime string) (ok bool, msg string) {
	backups := this.GetGlobalArrMapConfig("backups") // [{}, {}]
	var i int
	var backup map[string]string
	for i, backup = range backups {
		if backup["createdTime"] == createdTime {
			break;
		}
	}
	if i == len(backups) {
		return false, "Backup Not Found"
	}
	
	// 先备份当前
	ok, msg = this.Backup("Auto backup when restore from " + backup["createdTime"] )
	if !ok {
		return
	}
	
	// mongorestore -h localhost -d leanote --directoryperdb /home/user1/gopackage/src/github.com/leanote/leanote/mongodb_backup/leanote_install_data/
	binPath := configService.GetGlobalStringConfig("mongorestorePath")
	config := revel.Config;
	dbname, _ := config.String("db.dbname")
	host, _ := revel.Config.String("db.host")
	port, _ := revel.Config.String("db.port")
	username, _ := revel.Config.String("db.username")
	password, _ := revel.Config.String("db.password")
	// mongorestore -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM
	binPath = binPath + " --drop -h " + host + " -d " + dbname + " -port " + port
	if username != "" {
		binPath += " -u " + username + " -p " + password
	}
	
	path := backup["path"] + "/" + dbname
	// 判断路径是否存在
	if !IsDirExists(path) {
		return false, path + " Is Not Exists"
	}
	
	binPath += " --directoryperdb " + path
	
	cmd := exec.Command("/bin/sh", "-c", binPath)
	Log(binPath);
	b, err := cmd.Output()
    if err != nil {
    	msg = fmt.Sprintf("%v", err)
		ok = false
    	Log("error:......")
    	Log(string(b))
    	return
    }
	
	return true, ""
}
func (this *ConfigService) DeleteBackup(createdTime string) (bool, string) {
	backups := this.GetGlobalArrMapConfig("backups") // [{}, {}]
	var i int
	var backup map[string]string
	for i, backup = range backups {
		if backup["createdTime"] == createdTime {
			break;
		}
	}
	if i == len(backups) {
		return false, "Backup Not Found"
	}
	
	// 删除文件夹之
	err := os.RemoveAll(backups[i]["path"])
	if err != nil {
		return false, fmt.Sprintf("%v", err)
	}
	
	// 删除之
	backups = append(backups[0:i], backups[i+1:]...)
	
	ok := this.UpdateGlobalArrMapConfig(this.adminUserId, "backups", backups)
	return ok, ""
}

func (this *ConfigService) UpdateBackupRemark(createdTime, remark string) (bool, string) {
	backups := this.GetGlobalArrMapConfig("backups") // [{}, {}]
	var i int
	var backup map[string]string
	for i, backup = range backups {
		if backup["createdTime"] == createdTime {
			break;
		}
	}
	if i == len(backups) {
		return false, "Backup Not Found"
	}
	backup["remark"] = remark;
	
	ok := this.UpdateGlobalArrMapConfig(this.adminUserId, "backups", backups)
	return ok, ""
}

// 得到备份
func (this *ConfigService) GetBackup(createdTime string) (map[string]string, bool) {
	backups := this.GetGlobalArrMapConfig("backups") // [{}, {}]
	var i int
	var backup map[string]string
	for i, backup = range backups {
		if backup["createdTime"] == createdTime {
			break;
		}
	}
	if i == len(backups) {
		return map[string]string{}, false
	}
	return backup, true
}

//--------------
// sub domain
var defaultDomain string
var schema = "http://"
var port string

func init() {
	revel.OnAppStart(func() {
		port  = strconv.Itoa(revel.HttpPort)
		if port != "80" {
			port = ":" + port
		} else {
			port = "";
		}
		
		siteUrl, _ := revel.Config.String("site.url") // 已包含:9000, http, 去掉成 leanote.com
		if strings.HasPrefix(siteUrl, "http://") {
			defaultDomain = siteUrl[len("http://"):]
		} else if strings.HasPrefix(siteUrl, "https://") {
			defaultDomain = siteUrl[len("https://"):]
			schema = "https://"
		}
	})
}


func (this *ConfigService) GetSchema() string {
	return schema;
}
// 默认
func (this *ConfigService) GetDefaultDomain() string {
	return defaultDomain
}
// 包含http://
func (this *ConfigService) GetDefaultUrl() string {
	return schema + defaultDomain
}
// note
func (this *ConfigService) GetNoteDomain() string {
	subDomain := this.GetGlobalStringConfig("noteSubDomain");
	if subDomain != "" {
		return subDomain + port
	}
	return this.GetDefaultDomain() + "/note"
}
func (this *ConfigService) GetNoteUrl() string {
	return schema + this.GetNoteDomain();
}

// blog
func (this *ConfigService) GetBlogDomain() string {
	subDomain := this.GetGlobalStringConfig("blogSubDomain");
	if subDomain != "" {
		return subDomain + port
	}
	return this.GetDefaultDomain() + "/blog"
}
func (this *ConfigService) GetBlogUrl() string {
	return schema + this.GetBlogDomain();
}
// lea
func (this *ConfigService) GetLeaDomain() string {
	subDomain := this.GetGlobalStringConfig("leaSubDomain");
	if subDomain != "" {
		return subDomain + port
	}
	return this.GetDefaultDomain() + "/lea"
}
func (this *ConfigService) GetLeaUrl() string {
	return schema + this.GetLeaDomain();
}

func (this *ConfigService) GetUserUrl(domain string) string {
	return schema + domain + port
}
func (this *ConfigService) GetUserSubUrl(subDomain string) string {
	return schema + subDomain + "." + this.GetDefaultDomain()
}

// 是否允许自定义域名
func (this *ConfigService) AllowCustomDomain() bool {
	return configService.GetGlobalStringConfig("allowCustomDomain") != ""
}
// 是否是好的自定义域名
func (this *ConfigService) IsGoodCustomDomain(domain string) bool {
	blacks := this.GetGlobalArrayConfig("blackCustomDomains")
	for _, black := range blacks {
		if strings.Contains(domain, black) {
			return false
		}
	}
	return true
}
func (this *ConfigService) IsGoodSubDomain(domain string) bool {
	blacks := this.GetGlobalArrayConfig("blackSubDomains")
	LogJ(blacks)
	for _, black := range blacks {
		if domain == black {
			return false
		}
	}
	return true
}

// 上传大小
func (this *ConfigService) GetUploadSize(key string) float64 {
	f, _ := strconv.ParseFloat(this.GetGlobalStringConfig(key), 64)
	return f;
}
func (this *ConfigService) GetUploadSizeLimit() map[string]float64 {
	return map[string]float64{
		"uploadImageSize": this.GetUploadSize("uploadImageSize"),
		"uploadBlogLogoSize":this.GetUploadSize("uploadBlogLogoSize"),
		"uploadAttachSize":this.GetUploadSize("uploadAttachSize"),
		"uploadAvatarSize":this.GetUploadSize("uploadAvatarSize"),
	}
}
// 为用户得到全局的配置
// NoteController调用
func (this *ConfigService) GetGlobalConfigForUser() map[string]interface{} {
	uploadSizeConfigs := this.GetUploadSizeLimit();
	config := map[string]interface{}{}
	for k, v := range uploadSizeConfigs {
		config[k] = v
	}
	return config
}