package comm

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/jessevdk/go-flags"
	"github.com/kardianos/osext"
	"github.com/nats-io/nats.go"
	"github.com/spf13/viper"
)

type AppRootOption struct {
	Version  string `short:"v" long:"version" description:"conf version"`
	NatsPort int    `short:"p" long:"port" description:"conf nats port"`
	Data     string `short:"d" long:"data" description:"conf data dir"`
	Socket   int    `short:"s" long:"socket" description:"config quit tcp socket"`
}

type SysBusProfile struct {
	NatsWsPort int
	NatsPort   int
	MiniPort   int
	DbPort     int
}

func (o *AppRootOption) Parse() {
	flags.NewParser(o, flags.Default|flags.IgnoreUnknown).Parse()
}

var GAppOption = &AppRootOption{Data: "./data", Version: "0.0.0"}

type AppRoot struct {
	ExeDir      string
	DataDir     string
	Name        string
	ExitCode    int
	RunExe      func(func()) error
	OnQuitExe   func()
	Quit        chan struct{}
	HttpPort    int
	NatsWsPort  int
	NatsPort    int
	ConfYaml    string
	ParseConfig func(*viper.Viper) error
	Open        func() error
	SysBus      SysBusProfile

	Bus *NatsBus
}

func (a *AppRoot) InitBus(bus *NatsBus) error {

	fpath := path.Join(filepath.Dir(a.ExeDir), fmt.Sprintf("%s.exe", a.Name))
	appId := GetAppPathId(fpath)
	fmt.Println("app id==========", appId, fpath)

	p, e := bus.RequestConfig("sysbus.profile")
	if e != nil {
		fmt.Println("获取配置失败!", e)
		return e
	}
	e = json.Unmarshal([]byte(p), &a.SysBus)
	if e != nil {
		fmt.Println("解析配置失败!", e)
		return e
	}
	a.NatsWsPort = a.SysBus.NatsWsPort

	_, e = bus.QueueSubscribe(&SubOption{
		Sub: "app.quit." + appId,
		Call: func(obj interface{}, msg *nats.Msg) interface{} {
			a.QuitApp(0)
			return "ok"
		},
	})

	if e != nil {
		return e
	}
	_, e = bus.QueueSubscribe(&SubOption{
		Sub: "app.open." + appId,
		Call: func(obj interface{}, msg *nats.Msg) interface{} {
			d := NatsResponse{ErrorNo: 400}
			if a.Open == nil {
				d.ErrorDesc = "当前应用不能打开"
			} else {
				e := a.Open()
				if e != nil {
					d.ErrorDesc = e.Error()
				} else {
					d.ErrorNo = 200
					d.ErrorDesc = ""
				}
			}
			return d
		},
	})
	if e != nil {
		return e
	}

	e = LancherClientRun(bus, fpath, path.Join(a.ExeDir, "app.yaml"), "")
	if e != nil {
		return e
	}

	a.Bus = bus

	return nil
}

func (a *AppRoot) QuitApp(exit int) {
	if a.OnQuitExe != nil {
		a.OnQuitExe()
		a.OnQuitExe = nil
	}
	a.ExitCode = exit
	a.Quit <- struct{}{}
}
func (a *AppRoot) GetNatsUrl() string {
	return fmt.Sprintf("nats://localhost:%d", a.NatsPort)
}
func (a *AppRoot) GetNatsWebUrl() string {
	return fmt.Sprintf("ws://localhost:%d", a.NatsWsPort)
}

func (a *AppRoot) GenHttpPort(d int) int {
	portFile := filepath.Join(a.DataDir, fmt.Sprintf("%s.port", a.Name))
	portStr, err := ioutil.ReadFile(portFile)
	if err == nil && len(portStr) > 0 {
		port, _ := strconv.Atoi(string(portStr))
		if port > 0 {
			return port
		}
	}
	port, _ := GetFreePort()
	if port > 0 {
		d = port
	}
	os.MkdirAll(GAppOption.Data, os.ModePerm)
	ioutil.WriteFile(portFile, []byte(fmt.Sprintf("%d", d)), os.ModePerm)
	return d
}

func (a *AppRoot) Run() {
	GAppOption.Parse()

	e := InitQuitSocket(GAppOption.Socket)
	if e != nil {
		fmt.Println(e)
		return
	}

	//go run . 执行
	f, _ := osext.ExecutableFolder()
	exeFoloder := ""
	if strings.LastIndex(f, "\\go-build") == -1 {
		exeFoloder = f
	}

	a.ExeDir = exeFoloder
	a.HttpPort = a.GenHttpPort(20000)
	a.DataDir = GAppOption.Data

	a.NatsPort = GAppOption.NatsPort

	cfile := filepath.Join(exeFoloder, "app.yaml")

	a.ConfYaml = cfile

	if isExist(cfile) && a.ParseConfig != nil {
		file, err := os.Open(cfile)
		if err != nil {
			fmt.Println("open app.yaml error=>", cfile, err)
			return
		}
		v := viper.New()
		v.SetConfigType("yaml")
		err = v.ReadConfig(file)
		if err != nil {
			fmt.Println("read app.yaml error=>", cfile, err)
			return
		}
		err = a.ParseConfig(v)
		if err != nil {
			fmt.Println("parse config error=>", err)
			return
		}
	}

	a.Quit = make(chan struct{})
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("系统异常", r)
			a.QuitApp(500)
		}
	}()

	//开启一个线程跑主要流畅
	go func() {
		err := a.RunExe(func() {
			fmt.Printf("%s started\n", a.Name)
			fmt.Printf("%s is running\n", a.Name)
		})

		if err != nil {
			fmt.Println(a.Name, " running error=>", err)
			a.ExitCode = 1
			a.Quit <- struct{}{}
		} else {
			<-QuitChan
			a.QuitApp(0)
		}
	}()
	<-a.Quit

	if a.OnQuitExe != nil {
		a.OnQuitExe()
	}

	fmt.Println(a.Name, " System Quited")
	if a.ExitCode > 0 {
		os.Exit(a.ExitCode)
	}
	if a.Bus != nil {
		LancherClientQuitApp(a.Bus)
	}
}

func isExist(path string) bool {
	_, err := os.Stat(path)
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		if os.IsNotExist(err) {
			return false
		}
		return false
	}
	return true
}

var GApp = &AppRoot{}
