package client

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"git.qianqiusoft.com/qianqiusoft/light-apiengine/env"
	"git.qianqiusoft.com/qianqiusoft/light-apiengine/utils"
	"git.qianqiusoft.com/qianqiusoft/light-apiengine-client/code_gen"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

type EngineClient struct {
	ProjectName string
	ServerUrl   string
}

type ResponeResult struct {
	Code int32 `json:"code"`
	//描述
	Msg string `json:"msg"`
	//数据
	Data []GenerateResult `json:"data"`
}
type GenerateResult struct {
	Name    string `json:"name"`
	Content []byte `json:"content"`
	Type    string `json:"type"`
}

func NewEngineClient(project_name string, server_url string) *EngineClient {
	return &EngineClient{project_name, server_url}
}

func (c *EngineClient) InitDefalutFile(project_name string) {
	c.ProjectName = project_name
	os.MkdirAll("controllers/gen", os.ModePerm)
	os.MkdirAll("controllers/partial", os.ModePerm)
	os.MkdirAll("models", os.ModePerm)
	os.MkdirAll("routers", os.ModePerm)
	os.MkdirAll("conf", os.ModePerm)
	os.MkdirAll("sqlconfig", os.ModePerm)
	//os.MkdirAll("sqlconfig/gen", os.ModePerm)
	os.MkdirAll("doc", os.ModePerm)
	os.MkdirAll("web", os.ModePerm)

	_, err := os.Stat(project_name + ".xml")
	if os.IsNotExist(err) {
		xml := strings.Replace(DefaultProjectXML, "{project_name}", project_name, -1)
		ioutil.WriteFile(project_name+".xml", []byte(xml), os.ModePerm)
	}

	_, err = os.Stat(project_name + ".xsd")
	if os.IsNotExist(err) {
		ioutil.WriteFile(project_name+".xsd", []byte(XSD), os.ModePerm)
	}

	_, err = os.Stat("Dockerfile")
	if os.IsNotExist(err) {
		xml := strings.Replace(DockerFile, "{project_name}", project_name, -1)
		ioutil.WriteFile("Dockerfile", []byte(xml), os.ModePerm)
	}
	_, err = os.Stat("build_docker.sh")
	if os.IsNotExist(err) {
		xml := strings.Replace(BuildSH, "{project_name}", project_name, -1)
		ioutil.WriteFile("build_docker.sh", []byte(xml), os.ModePerm)
	}

	_, err = os.Stat("README.md")
	if os.IsNotExist(err) {
		xml := strings.Replace(MARK_DOWN, "{project_name}", project_name, -1)
		xml = strings.Replace(xml, "{!}", "`", -1)
		ioutil.WriteFile("README.md", []byte(xml), os.ModePerm)
	}
}

func (c *EngineClient) GenerateCurrentProject() {
	c.GenerateCurrentProjectToPath("")
}

func (c *EngineClient) GenerateCurrentProjectToPath(destPath string) {
	c.InitDefalutFile(c.ProjectName)

	// 0---0
	//path, _ := utils.GetCurrentPath()
	//c.Generate(path + c.ProjectName + ".xml")

	projMainXmlFileTemp := c.MergeXmlToSingle()
	if projMainXmlFileTemp == "" {
		fmt.Println("projMainXmlFileTemp is empty")
		return
	}

	c.GenerateToPath(projMainXmlFileTemp, destPath)
	//c.GenSwagger(projMainXmlFileTemp)

	path, _ := utils.GetCurrentPath()
	CopyDir(env.Get("GOPATH", "") + "/src/git.qianqiusoft.com/qianqiusoft/light-apiengine/sqlconfig",
		path+"vendor/git.qianqiusoft.com/qianqiusoft/light-apiengine/sqlconfig",
		"./vendor/git.qianqiusoft.com/qianqiusoft/light-apiengine/sqlconfig",
		"../src/git.qianqiusoft.com/qianqiusoft/light-apiengine/sqlconfig",	
		path+"sqlconfig")
}

func (c *EngineClient) GenerateToPath(xmlfile string, dest_path string) {
	var result ResponeResult

	isCompress := true

	var bJson []byte

	if c.ServerUrl == "local" {

		isCompress = false
		//dest_path+="../i2-erp-compare/"
		bs := code_gen.DoGenerate(xmlfile, isCompress)
		bJson = bs
	} else {
		server := "http://qianqiusoft.com:6166"
		if c.ServerUrl != "" {
			server = c.ServerUrl
		}
		server += "/api/v1/develop/generate"

		bs := DoRequest(xmlfile, server)
		bJson = bs.Bytes()
	}

	if bJson != nil {
		err := json.Unmarshal(bJson, &result)
		if err != nil {
			fmt.Println(err.Error())
		}
		if isCompress {
			for i := 0; i < len(result.Data); i++ {
				var b bytes.Buffer
				b.Write(result.Data[i].Content)
				unzip := unzipbytes(&b)
				result.Data[i].Content = unzip.Bytes()
			}
		}
		for i := 0; i < len(result.Data); i++ {
			path := result.Data[i].Name
			//fmt.Println(path)
			path = path[len(c.ProjectName)+1:]
			path = dest_path + path
			fmt.Println(path)

			ft := result.Data[i].Type
			if ft == "main" {
				//fmt.Println(string(result.Data[i].Content))
			} else if ft == "config" || ft == "ci" {
				_, err := os.Stat(path)
				if err == nil {
					fmt.Println(path + "已经存在，忽略...")
					// fmt.Println(result.Data[i].Content)
				} else {
					ioutil.WriteFile(path, result.Data[i].Content, os.ModePerm)
				}
			} else if ft == "controllers" {
				os.MkdirAll(filepath.Dir(path), os.ModePerm)
				if strings.Index(path, "_gen.go") > 0 {
					ioutil.WriteFile(path, result.Data[i].Content, os.ModePerm)
				} else {
					_, err := os.Stat(path)
					if err == nil {
						ioutil.WriteFile(path+"_new", result.Data[i].Content, os.ModePerm)
					} else if os.IsNotExist(err) {
						ioutil.WriteFile(path, result.Data[i].Content, os.ModePerm)
					}
				}
			} else if ft == "routers" {
				os.MkdirAll(filepath.Dir(path), os.ModePerm)
				ioutil.WriteFile(path, result.Data[i].Content, os.ModePerm)
			} else if ft == "sql" {
				if strings.Index(path, "_gen.xml") > 0 {
					os.MkdirAll(filepath.Dir(path), os.ModePerm)
					ioutil.WriteFile(path, result.Data[i].Content, os.ModePerm)
				} else {
					_, err := os.Stat(path)
					if err == nil {
						//ioutil.WriteFile(path+"_new", result.Data[i].Content, os.ModePerm)
					} else if os.IsNotExist(err) {
						os.MkdirAll(filepath.Dir(path), os.ModePerm)
						ioutil.WriteFile(path, result.Data[i].Content, os.ModePerm)
					}
				}
			} else {
				os.MkdirAll(filepath.Dir(path), os.ModePerm)
				err := ioutil.WriteFile(path, result.Data[i].Content, os.ModePerm)
				if err != nil {
					fmt.Println(err.Error())
				}
			}
		}
	}
}

/*
func (c *EngineClient) Generate(xmlfile string) {
	c.GenerateToPath(xmlfile, "")
	//c.GenSwagger(xmlfile)
}
*/

func (c *EngineClient) GenSwagger(xmlfile string) {
	server := "http://swagger.pusher.i2erp.cn"
	server += "/api/v1/upload"

	fmt.Println("===========================================================================>11")

	request, err := newfileUploadRequest(server, nil, "xmlfile", xmlfile)
	if err != nil {
		fmt.Println(err)
	}
	client := &http.Client{}
	resp, err := client.Do(request)
	if err != nil {
		fmt.Println("===========================================================================>", err.Error())
	} else {
		defer resp.Body.Close()
		bytess, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println("=======================================>ioutil.ReadAll error", err.Error())
		} else {
			fmt.Println("=======================================>", string(bytess))
		}
	}
}

/**
 * @brief: merge xml
 * @param: none
 * @return: the path of final xml
 */
func (c *EngineClient) MergeXmlToSingle() string {
	path, _ := utils.GetCurrentPath()
	// e.g.: c:/gopath/src/hanghua_background_proj
	projDir := fmt.Sprintf("%s%s.proj", path, c.ProjectName)
	projMainXmlFile := projDir + "/" + c.ProjectName + ".xml"
	projMainXmlFileTemp := projDir + "/" + c.ProjectName + "_temp.xml"
	_, err := os.Stat(projMainXmlFileTemp)
	if os.IsNotExist(err) {
		fmt.Println("-------------------------->remove project main file temp")
		os.Remove(projMainXmlFileTemp) // remove
	}

	_, err = os.Stat(projMainXmlFile)
	if os.IsNotExist(err) {
		fmt.Println("main xml file of " + c.ProjectName + " does not exist")
		return ""
	}

	app := XmlApplication{}
	bytess, _ := ioutil.ReadFile(projMainXmlFile)
	err = xml.Unmarshal(bytess, &app)
	if err != nil {
		fmt.Println("xml.Unmarshal(bytess, &app) error " + err.Error())
		return ""
	}

	controllers, err := scanControllers(projDir + "/controllers")
	if err != nil {
		fmt.Println("scanControllers error " + err.Error())
		return ""
	}

	beans, err := scanBeans(projDir + "/beans")
	if err != nil {
		fmt.Println("scanBeans error " + err.Error())
		return ""
	}

	tables, err := scanTables(projDir + "/tables")
	if err != nil {
		fmt.Println("scanTables error " + err.Error())
		return ""
	}

	if app.Controllers.ControllerList == nil {
		app.Controllers.ControllerList = []XmlController{}
	}
	for i := range controllers {
		app.Controllers.ControllerList = append(app.Controllers.ControllerList, controllers[i])
	}
	createVueApisFolder(app.Controllers.ControllerList)

	if app.Beans.BeanList == nil {
		app.Beans.BeanList = []XmlBean{}
	}
	for i := range beans {
		app.Beans.BeanList = append(app.Beans.BeanList, beans[i])
	}
	if app.Tables.TableList == nil {
		app.Tables.TableList = []XmlTable{}
	}
	for i := range tables {
		app.Tables.TableList = append(app.Tables.TableList, tables[i])
	}

	bytess, err = xml.Marshal(app)
	if err != nil {
		fmt.Println("xml.Marshal(app) error " + err.Error())
		return ""
	}
	err = ioutil.WriteFile(projMainXmlFileTemp, bytess, os.ModePerm)
	if err != nil {
		fmt.Println("ioutil.WriteFile(projMainXmlFileTemp, bytess,os.ModePerm) error " + err.Error())
		return ""
	}
	_, err = os.Stat(projMainXmlFileTemp)
	if os.IsNotExist(err) {
		fmt.Println("main xml file temp of " + c.ProjectName + " does not exist")
		return ""
	} else {
		fmt.Println("main xml file temp of " + c.ProjectName + " exist")
	}

	return projMainXmlFileTemp
}

func scanControllers(ctrldir string) ([]XmlController, error) {
	_, err := os.Stat(ctrldir)
	if os.IsNotExist(err) {
		fmt.Println("controller dir does not exist", err.Error())
		return nil, err
	}

	controllers := []XmlController{}
	filePaths := []string{}
	filePaths, err = getAllFile(strings.TrimSuffix(ctrldir, "/"), filePaths)
	if err != nil {
		fmt.Println("controller getAllFile error", err.Error())
		return nil, err
	}
	for i := range filePaths {
		ctrl := XmlController{}
		ctrlfile := filePaths[i]
		_, err = os.Stat(ctrlfile)
		if os.IsNotExist(err) {
			fmt.Println("controller xml file " + ctrlfile + " does not exist")
			return controllers, err
		}

		ctrlfile = strings.Replace(ctrlfile, "\\", "/", -1)
		arr := strings.Split(ctrlfile, "/")

		bytess, _ := ioutil.ReadFile(ctrlfile)
		err = xml.Unmarshal(bytess, &ctrl)
		if err != nil {
			fmt.Println("xml.Unmarshal(bytess, &app) error " + err.Error())
			return controllers, err
		}

		// get sub dir name
		if arr[len(arr)-2] != "controllers" {
			// if sub dir is not controllers, set the dir attr
			ctrl.Dir = arr[len(arr)-2]
		}
		controllers = append(controllers, ctrl)
	}
	return controllers, nil

	//err = filepath.Walk(ctrldir, func(path string, info os.FileInfo, err error) error {
	//
	//	if err != nil {
	//		return filepath.SkipDir
	//	}
	//	if info.IsDir(){
	//		return nil
	//	}
	//
	//	ctrl := XmlController{}
	//	ctrlfile := ctrldir + "/" + info.Name()
	//	_, err = os.Stat(ctrlfile)
	//	if os.IsNotExist(err) {
	//		fmt.Println("controller xml file " + ctrlfile + " does not exist")
	//		return filepath.SkipDir
	//	}
	//	bytess, _ := ioutil.ReadFile(ctrlfile)
	//	err = xml.Unmarshal(bytess, &ctrl)
	//	if err != nil{
	//		fmt.Println("xml.Unmarshal(bytess, &app) error " + err.Error())
	//		return filepath.SkipDir
	//	}
	//	controllers = append(controllers, ctrl)
	//
	//	return nil
	//})

	//return controllers, err
}

func scanBeans(ctrldir string) ([]XmlBean, error) {
	_, err := os.Stat(ctrldir)
	if os.IsNotExist(err) {
		fmt.Println("controller dir does not exist")
		return nil, err
	}

	beans := []XmlBean{}
	filePaths := []string{}
	filePaths, err = getAllFile(strings.TrimSuffix(ctrldir, "/"), filePaths)
	if err != nil {
		fmt.Println("controller getAllFile error", err.Error())
		return nil, err
	}
	for i := range filePaths {
		ctrl := XmlBean{}
		ctrlfile := filePaths[i]
		_, err = os.Stat(ctrlfile)
		if os.IsNotExist(err) {
			fmt.Println("controller xml file " + ctrlfile + " does not exist")
			return beans, err
		}
		bytess, _ := ioutil.ReadFile(ctrlfile)
		err = xml.Unmarshal(bytess, &ctrl)
		if err != nil {
			fmt.Println("xml.Unmarshal(bytess, &app) error " + err.Error())
			return beans, err
		}
		beans = append(beans, ctrl)
	}
	return beans, nil

	//err = filepath.Walk(ctrldir, func(path string, info os.FileInfo, err error) error {
	//
	//	if err != nil {
	//		return filepath.SkipDir
	//	}
	//	if info.IsDir(){
	//		return nil
	//	}
	//
	//	bean := XmlBean{}
	//	beanfile := ctrldir + "/" + info.Name()
	//	_, err = os.Stat(beanfile)
	//	if os.IsNotExist(err) {
	//		fmt.Println("controller xml file " + beanfile + " does not exist")
	//		return filepath.SkipDir
	//	}
	//	bytess, _ := ioutil.ReadFile(beanfile)
	//	err = xml.Unmarshal(bytess, &bean)
	//	if err != nil{
	//		fmt.Println("xml.Unmarshal(bytess, &app) error " + err.Error())
	//		return filepath.SkipDir
	//	}
	//	beans = append(beans, bean)
	//
	//	return nil
	//})
	//
	//return beans, err
}

func scanTables(ctrldir string) ([]XmlTable, error) {
	_, err := os.Stat(ctrldir)
	if os.IsNotExist(err) {
		fmt.Println("controller dir does not exist")
		return nil, err
	}

	tables := []XmlTable{}
	filePaths := []string{}
	filePaths, err = getAllFile(strings.TrimSuffix(ctrldir, "/"), filePaths)
	if err != nil {
		fmt.Println("controller getAllFile error", err.Error())
		return nil, err
	}
	for i := range filePaths {
		ctrl := XmlTable{}
		ctrlfile := filePaths[i]
		_, err = os.Stat(ctrlfile)
		if os.IsNotExist(err) {
			fmt.Println("controller xml file " + ctrlfile + " does not exist")
			return tables, err
		}
		bytess, _ := ioutil.ReadFile(ctrlfile)
		err = xml.Unmarshal(bytess, &ctrl)
		if err != nil {
			fmt.Println("xml.Unmarshal(bytess, &app) error " + err.Error())
			return tables, err
		}
		tables = append(tables, ctrl)
	}
	return tables, nil

	//err = filepath.Walk(ctrldir, func(path string, info os.FileInfo, err error) error {
	//
	//	if err != nil {
	//		return filepath.SkipDir
	//	}
	//	if info.IsDir(){
	//		return nil
	//	}
	//
	//	table := XmlTable{}
	//	tablefile := ctrldir + "/" + info.Name()
	//	_, err = os.Stat(tablefile)
	//	if os.IsNotExist(err) {
	//		fmt.Println("controller xml file " + tablefile + " does not exist")
	//		return filepath.SkipDir
	//	}
	//	bytess, _ := ioutil.ReadFile(tablefile)
	//	err = xml.Unmarshal(bytess, &table)
	//	if err != nil{
	//		fmt.Println("xml.Unmarshal(bytess, &app) error " + err.Error())
	//		return filepath.SkipDir
	//	}
	//	tables = append(tables, table)
	//
	//	return nil
	//})
	//
	//return tables, err
}

func createVueApisFolder(controllers []XmlController) {
	for i := range controllers {
		fmt.Println("------------------------------>" + controllers[i].Name)
		os.MkdirAll("vue/api/modules/"+controllers[i].Name, os.ModePerm)
	}
}

func getAllFile(pathname string, s []string) ([]string, error) {
	rd, err := ioutil.ReadDir(pathname)
	if err != nil {
		fmt.Println("read dir fail:", err)
		return s, err
	}
	for _, fi := range rd {
		if fi.IsDir() {
			fullDir := pathname + "/" + fi.Name()
			s, err = getAllFile(fullDir, s)
			if err != nil {
				fmt.Println("read dir fail:", err)
				return s, err
			}
		} else {
			if strings.HasSuffix(strings.ToLower(fi.Name()), ".xml") {
				fullName := pathname + "/" + fi.Name()
				s = append(s, fullName)
			}
		}
	}
	return s, nil
}

func unzipbytes(bs *bytes.Buffer) bytes.Buffer {
	r, _ := gzip.NewReader(bs)
	defer r.Close()
	var b bytes.Buffer
	b.ReadFrom(r)
	//undatas, _ := ioutil.ReadAll(r)
	//fmt.Println("ungzip size:", len(undatas))
	return b
}

func DoRequest(xmlfile string, server string) *bytes.Buffer {

	request, err := newfileUploadRequest(server, nil, "xmlfile", xmlfile)
	if err != nil {
		fmt.Println(err)
	}
	client := &http.Client{}
	resp, err := client.Do(request)
	if err != nil {
		fmt.Println(err)
	} else {
		body := &bytes.Buffer{}
		_, err := body.ReadFrom(resp.Body)
		if err != nil {
			fmt.Println(err)
		}
		resp.Body.Close()
		fmt.Println(resp.StatusCode)
		fmt.Println(resp.Header)

		//fmt.Println(body)
		return body
	}
	return nil
}

func newfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(paramName, filepath.Base(path))
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(part, file)

	for key, val := range params {
		_ = writer.WriteField(key, val)
	}
	err = writer.Close()
	if err != nil {
		return nil, err
	}

	request, err := http.NewRequest("POST", uri, body)
	request.Header.Add("Content-Type", writer.FormDataContentType())
	return request, err
}
