package route

import (
	"encoding/json"
	"encoding/xml"
	"strconv"
	"time"

	"github.com/IceWhaleTech/CasaOS/model"
	"github.com/IceWhaleTech/CasaOS/model/system_app"
	"github.com/IceWhaleTech/CasaOS/pkg/config"
	"github.com/IceWhaleTech/CasaOS/pkg/docker"
	"github.com/IceWhaleTech/CasaOS/pkg/utils/env_helper"
	"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
	"github.com/IceWhaleTech/CasaOS/pkg/utils/port"
	"github.com/IceWhaleTech/CasaOS/service"
	model2 "github.com/IceWhaleTech/CasaOS/service/model"
	uuid "github.com/satori/go.uuid"
)

func InitFunction() {
	go checkSystemApp()
}

var syncIsExistence = false

func installSyncthing(appId string) {

	var appInfo model.ServerAppList
	m := model.CustomizationPostData{}
	var dockerImage string
	var dockerImageVersion string

	appInfo = service.MyService.OAPI().GetServerAppInfo(appId)

	dockerImage = appInfo.Image

	if len(appInfo.ImageVersion) == 0 {
		dockerImageVersion = "latest"
	}

	if appInfo.NetworkModel != "host" {
		for i := 0; i < len(appInfo.Ports); i++ {
			if p, _ := strconv.Atoi(appInfo.Ports[i].ContainerPort); port.IsPortAvailable(p, appInfo.Ports[i].Protocol) {
				appInfo.Ports[i].CommendPort = strconv.Itoa(p)
			} else {
				if appInfo.Ports[i].Protocol == "tcp" {
					if p, err := port.GetAvailablePort("tcp"); err == nil {
						appInfo.Ports[i].CommendPort = strconv.Itoa(p)
					}
				} else if appInfo.Ports[i].Protocol == "upd" {
					if p, err := port.GetAvailablePort("udp"); err == nil {
						appInfo.Ports[i].CommendPort = strconv.Itoa(p)
					}
				}
			}

			if appInfo.Ports[i].Type == 0 {
				appInfo.PortMap = appInfo.Ports[i].CommendPort
			}
		}
	}

	for i := 0; i < len(appInfo.Devices); i++ {
		if !file.CheckNotExist(appInfo.Devices[i].ContainerPath) {
			appInfo.Devices[i].Path = appInfo.Devices[i].ContainerPath
		}
	}
	if len(appInfo.Tip) > 0 {
		appInfo.Tip = env_helper.ReplaceStringDefaultENV(appInfo.Tip)
	}

	for i := 0; i < len(appInfo.Volumes); i++ {
		appInfo.Volumes[i].Path = docker.GetDir("", appInfo.Volumes[i].ContainerPath)
	}
	appInfo.MaxMemory = service.MyService.ZiMa().GetMemInfo().Total >> 20

	id := uuid.NewV4().String()

	installLog := model2.AppNotify{}

	// step：下载镜像
	err := service.MyService.Docker().DockerPullImage(dockerImage+":"+dockerImageVersion, installLog)
	if err != nil {
		//pull image error
		return
	}

	for !service.MyService.Docker().IsExistImage(dockerImage + ":" + dockerImageVersion) {
		time.Sleep(time.Second)
	}

	m.CpuShares = 50
	m.Envs = appInfo.Envs
	m.Memory = int64(appInfo.MaxMemory)
	m.Origin = "system"
	m.PortMap = appInfo.PortMap
	m.Ports = appInfo.Ports
	m.Restart = ""
	m.Volumes = appInfo.Volumes

	containerId, err := service.MyService.Docker().DockerContainerCreate(dockerImage+":"+dockerImageVersion, id, m, appInfo.NetworkModel)

	if err != nil {
		// create container error
		return
	}

	//step：start container
	err = service.MyService.Docker().DockerContainerStart(id)
	if err != nil {
		//start container error
		return
	}

	portsStr, _ := json.Marshal(appInfo.Ports)
	envsStr, _ := json.Marshal(appInfo.Envs)
	volumesStr, _ := json.Marshal(appInfo.Volumes)
	devicesStr, _ := json.Marshal(appInfo.Devices)
	//step: 保存数据到数据库
	md := model2.AppListDBModel{
		CustomId: id,
		Title:    appInfo.Title,
		//ScreenshotLink: appInfo.ScreenshotLink,
		Slogan:      appInfo.Tagline,
		Description: appInfo.Description,
		//Tags:           appInfo.Tags,
		Icon:        appInfo.Icon,
		Version:     dockerImageVersion,
		ContainerId: containerId,
		Image:       dockerImage,
		Index:       appInfo.Index,
		PortMap:     appInfo.PortMap,
		Label:       appInfo.Title,
		EnableUPNP:  false,
		Ports:       string(portsStr),
		Envs:        string(envsStr),
		Volumes:     string(volumesStr),
		Position:    true,
		NetModel:    appInfo.NetworkModel,
		Restart:     m.Restart,
		CpuShares:   50,
		Memory:      int64(appInfo.MaxMemory),
		Devices:     string(devicesStr),
		Origin:      m.Origin,
		CreatedAt:   strconv.FormatInt(time.Now().Unix(), 10),
		UpdatedAt:   strconv.FormatInt(time.Now().Unix(), 10),
	}
	service.MyService.App().SaveContainer(md)

	checkSystemApp()
}

// check if the system application is installed
func checkSystemApp() {
	list := service.MyService.App().GetSystemAppList()
	for _, v := range *list {
		if v.Image == "linuxserver/syncthing" {
			syncIsExistence = true
			if config.SystemConfigInfo.SyncPort != v.Port {
				config.SystemConfigInfo.SyncPort = v.Port
			}
			var paths []model.PathMap
			json.Unmarshal([]byte(v.Volumes), &paths)
			path := ""
			for _, i := range paths {
				if i.ContainerPath == "/config" {
					path = docker.GetDir(v.CustomId, i.ContainerPath) + "config.xml"
					for i := 0; i < 10; i++ {
						if file.CheckNotExist(path) {
							time.Sleep(1 * time.Second)
						} else {
							break
						}
					}
					break
				}
			}
			content := file.ReadFullFile(path)
			syncConfig := &system_app.SyncConfig{}
			xml.Unmarshal(content, &syncConfig)
			config.SystemConfigInfo.SyncKey = syncConfig.Key
		}
	}
	if !syncIsExistence {
		installSyncthing("44")
	}
}
