package support

import (
	"flag"
	"fmt"
	"github.com/eyebluecn/tank/code/core"
	"github.com/eyebluecn/tank/code/tool/result"
	"github.com/eyebluecn/tank/code/tool/util"
	jsoniter "github.com/json-iterator/go"
	"golang.org/x/crypto/ssh/terminal"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"syscall"
)

const (
	//start web. This is the default mode.
	MODE_WEB = "web"
	//mirror local files to EyeblueTank.
	MODE_MIRROR = "mirror"
	//crawl remote file to EyeblueTank
	MODE_CRAWL = "crawl"
	//Current version.
	MODE_VERSION = "version"
)

type TankApplication struct {
	//mode
	mode string

	//EyeblueTank host and port  default: http://127.0.0.1:core.DEFAULT_SERVER_PORT
	host string
	//username
	username string
	//password
	password string

	//source file/directory different mode has different usage.
	src string
	//destination directory path(relative to root) in EyeblueTank
	dest string
	//true: overwrite, false:skip
	overwrite bool
	filename  string
}

//Start the application.
func (this *TankApplication) Start() {

	defer func() {
		if err := recover(); err != nil {
			fmt.Printf("ERROR:%v\r\n", err)
		}
	}()

	modePtr := flag.String("mode", this.mode, "cli mode web/mirror/crawl")
	hostPtr := flag.String("host", this.username, "tank host")
	usernamePtr := flag.String("username", this.username, "username")
	passwordPtr := flag.String("password", this.password, "password")
	srcPtr := flag.String("src", this.src, "src absolute path")
	destPtr := flag.String("dest", this.dest, "destination path in tank.")
	overwritePtr := flag.Bool("overwrite", this.overwrite, "whether same file overwrite")
	filenamePtr := flag.String("filename", this.filename, "filename when crawl")

	//flag.Parse() must invoke before use.
	flag.Parse()

	this.mode = *modePtr
	this.host = *hostPtr
	this.username = *usernamePtr
	this.password = *passwordPtr
	this.src = *srcPtr
	this.dest = *destPtr
	this.overwrite = *overwritePtr
	this.filename = *filenamePtr

	//default start as web.
	if this.mode == "" || strings.ToLower(this.mode) == MODE_WEB {

		this.HandleWeb()

	} else if strings.ToLower(this.mode) == MODE_VERSION {

		this.HandleVersion()

	} else {

		//default host.
		if this.host == "" {
			this.host = fmt.Sprintf("http://127.0.0.1:%d", core.DEFAULT_SERVER_PORT)
		}

		if this.username == "" {
			panic(result.BadRequest("in mode %s, username is required", this.mode))
		}

		if this.password == "" {

			if util.EnvDevelopment() {
				panic(result.BadRequest("If run in IDE, use -password yourPassword to input password"))
			} else {

				fmt.Print("Enter Password:")
				bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
				if err != nil {
					panic(err)
				}

				this.password = string(bytePassword)
				fmt.Println()
			}
		}

		if strings.ToLower(this.mode) == MODE_MIRROR {

			this.HandleMirror()

		} else if strings.ToLower(this.mode) == MODE_CRAWL {

			this.HandleCrawl()

		} else {
			panic(result.BadRequest("cannot handle mode %s \r\n", this.mode))
		}
	}

}

func (this *TankApplication) HandleWeb() {

	//Step 1. Logger
	tankLogger := &TankLogger{}
	core.LOGGER = tankLogger
	tankLogger.Init()
	defer tankLogger.Destroy()

	//Step 2. Configuration
	tankConfig := &TankConfig{}
	core.CONFIG = tankConfig
	tankConfig.Init()

	//Step 3. Global Context
	tankContext := &TankContext{}
	core.CONTEXT = tankContext
	tankContext.Init()
	defer tankContext.Destroy()

	//Step 4. Start http
	http.Handle("/", core.CONTEXT)
	core.LOGGER.Info("App started at http://localhost:%v", core.CONFIG.ServerPort())

	dotPort := fmt.Sprintf(":%v", core.CONFIG.ServerPort())
	err1 := http.ListenAndServe(dotPort, nil)
	if err1 != nil {
		log.Fatal("ListenAndServe: ", err1)
	}

}

func (this *TankApplication) HandleMirror() {

	if this.src == "" {
		panic("src is required")
	}
	if this.dest == "" {
		panic("dest is required")
	}

	fmt.Printf("start mirror %s to EyeblueTank %s\r\n", this.src, this.dest)

	urlString := fmt.Sprintf("%s/api/matter/mirror", this.host)

	params := url.Values{
		"srcPath":         {this.src},
		"destPath":        {this.dest},
		"overwrite":       {fmt.Sprintf("%v", this.overwrite)},
		core.USERNAME_KEY: {this.username},
		core.PASSWORD_KEY: {this.password},
	}

	response, err := http.PostForm(urlString, params)
	core.PanicError(err)

	bodyBytes, err := ioutil.ReadAll(response.Body)

	webResult := &result.WebResult{}

	err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(bodyBytes, webResult)
	if err != nil {
		fmt.Printf("error response format %s \r\n", err.Error())
		return
	}

	if webResult.Code == result.OK.Code {
		fmt.Println("success")
	} else {
		fmt.Printf("error %s\r\n", webResult.Msg)
	}

}

func (this *TankApplication) HandleCrawl() {

	if this.src == "" {
		panic("src is required")
	}
	if this.dest == "" {
		panic("dest is required")
	}

	if this.filename == "" {
		panic("filename is required")
	}

	fmt.Printf("crawl %s to EyeblueTank %s\r\n", this.src, this.dest)

	urlString := fmt.Sprintf("%s/api/matter/crawl", this.host)

	params := url.Values{
		"url":             {this.src},
		"destPath":        {this.dest},
		"filename":        {this.filename},
		core.USERNAME_KEY: {this.username},
		core.PASSWORD_KEY: {this.password},
	}

	response, err := http.PostForm(urlString, params)
	core.PanicError(err)

	bodyBytes, err := ioutil.ReadAll(response.Body)

	webResult := &result.WebResult{}

	err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(bodyBytes, webResult)
	if err != nil {
		fmt.Printf("Error response format %s \r\n", err.Error())
		return
	}

	if webResult.Code == result.OK.Code {
		fmt.Println("success")
	} else {
		fmt.Printf("error %s\r\n", webResult.Msg)
	}

}

//fetch the application version
func (this *TankApplication) HandleVersion() {

	fmt.Printf("EyeblueTank %s\r\n", core.VERSION)

}
