package comm

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/nats-io/nats.go"
	"github.com/spf13/viper"
)

const (
	APPSTATE_RUNNING   = "运行中"
	APPSTATE_INSTALLED = "已安装"
)

type App struct {
	Id      string
	Name    string
	Label   string
	Desc    string
	Icon    string
	IconUrl string
	Version string
	Type    string

	ExeFpath       string
	Pwd            string
	Args           string
	RegTime        time.Time
	LastRuningTime time.Time
	Status         string //运行中
}

func GetAppId() (string, error) {
	f, e := os.Executable()
	if e != nil {
		return "", e
	}
	_, exeName := filepath.Split(f)
	if strings.Contains(f, "go-build") {
		fmt.Println()
		pwd, e := os.Getwd()
		if e != nil {
			return "", e
		}
		f = path.Join(pwd, exeName)
	}
	fmt.Println("app path=>", f)
	md := md5.Sum([]byte(f))
	return fmt.Sprintf("%x", md), nil
}

func GetAppPathId(fpath string) string {
	md := md5.Sum([]byte(fpath))
	return fmt.Sprintf("%x", md)
}

func ParseConfigFile(filepath string) (*App, error) {
	file, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}

	v := viper.New()
	v.SetConfigType("yaml")
	err = v.ReadConfig(file)
	if err != nil {
		return nil, err
	}
	c := new(App)
	err = v.Unmarshal(c)
	return c, err
}

var _callCancel = func() {}

func CancelServeRun() {
	_callCancel()
}
func ServerRun(bus *NatsBus) error {
	cancel := WatchChange(bus)
	if cancel == nil {
		return fmt.Errorf("启动App监听失败")
	}
	_callCancel = cancel

	bus.Subscribe("lancher.apps.list", func(msg *nats.Msg) {
		out := []*App{}
		nc := bus.GetNatsConn()
		js, e := nc.JetStream()
		if e == nil {
			kv, e := js.CreateKeyValue(&nats.KeyValueConfig{Bucket: "lancher-apps"})
			if e == nil {
				out = getApps(kv)
			}
		}
		payload, _ := json.Marshal(out)
		msg.Respond(payload)
	})

	//启动app
	bus.Subscribe("lancher.start.*", func(msg *nats.Msg) {
		//获取AppId
		fmt.Println("starting=>", msg.Subject)
	})
	return nil
}

func WatchChange(bus *NatsBus) func() {
	nc := bus.GetNatsConn()
	js, e := nc.JetStream()
	if e != nil {
		fmt.Println(e)
		return nil
	}

	if e != nil {
		fmt.Println(e)
		return nil
	}

	kv, e := js.CreateKeyValue(&nats.KeyValueConfig{Bucket: "lancher-apps"})
	if e != nil {
		return nil
	}
	watch, e := kv.WatchAll()
	if e != nil {
		return nil
	}
	quiteCh := make(chan int, 1)

	go func() {
		for {
			conti := true
			select {
			case <-quiteCh:
				conti = false
			default:
				v := <-watch.Updates()
				if v != nil {
					fmt.Println("wathing update=>", v.Key(), v.Bucket(), v.Operation())
				}
				bus.PublishObj("lancher.apps.updated", getApps(kv))

				time.Sleep(time.Second)
			}
			if !conti {
				break
			}
		}
		fmt.Println("waching app quited")
	}()
	return func() {
		quiteCh <- 1
	}
}

func getApps(kv nats.KeyValue) []*App {
	out := []*App{}

	keys, e := kv.Keys()
	if e != nil {
		fmt.Println("keys error=>", e)
		return out
	}
	for _, key := range keys {
		v, _ := kv.Get(key)
		if v != nil {
			app := &App{}
			e := json.Unmarshal(v.Value(), app)
			if e == nil {
				fmt.Println("app=>", *app)
				out = append(out, app)
			} else {
				fmt.Println("Unmarshal errr=>", e)
			}
		}
	}
	return out
}
