package main

import (
	"bytes"
	"flag"
	"fmt"
	"github.com/tal-tech/go-zero/core/mapping"
	"github.com/tiptok/gocomm/common"
	"github.com/tiptok/gocomm/gs"
	"gopkg.in/yaml.v2"
	"html/template"
	"io/fs"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

const (
	ModulePrintModel ="print"
	ModulePrintGateway ="gateway"
)

var projectPath string
var useCase string

func init(){
	flag.StringVar(&useCase,"m","print","module 1.print(打印模型) 2.gateway(输出网关接口)")
	flag.StringVar(&projectPath,"p","F:\\go\\src\\mmm-go-pp\\terms\\document\\terms","项目描述文件根目录")
}
var attrMap map[string]*MetaDataItem = make(map[string]*MetaDataItem)
var schemaMap map[string]Schema = make(map[string]Schema)
var apiMap  map[string]ApiItem= make(map[string]ApiItem)
var apiKey []string

func main(){
	flag.Parse()
	schemaPath:=filepath.Join(projectPath,"schemas")
	attributesPath:=filepath.Join(projectPath,"attributes")
	apiPath:=filepath.Join(projectPath,"api")
	valid()
	initSchema(schemaPath)
	initAttr(attributesPath)
	initApi(apiPath)
	switch useCase {
	case ModulePrintModel:
		printSchema()
	case ModulePrintGateway:
		printGateWay()
	}
}

func valid(){
	if _,err:=os.Stat(projectPath);err!=nil{
		fmt.Println(err)
	}
}

func initSchema(schemaPath string){
	schemaFiles,_ :=ioutil.ReadDir(schemaPath)
	for _,f :=range schemaFiles{
		var schema Schema
		if f.IsDir(){
			continue
		}
		fr,_ :=os.Open(filepath.Join(schemaPath,f.Name()))
		if err :=mapping.UnmarshalYamlReader(fr,&schema);err!=nil{
			fmt.Printf("file:%v error:%v \n",f.Name(),err.Error())
			continue
		}
		schemaMap[schema.MetaData.Name] = schema
	}
}

func initAttr(attributesPath string){
	filepath.WalkDir(attributesPath,wrapperWalkDirFunc(readAttr))
}

func initApi(apiPath string){
	filepath.WalkDir(apiPath,wrapperWalkDirFunc(readApi))
}

func readAttr(f *os.File)error{
	var attr Attribute
	if err :=mapping.UnmarshalYamlReader(f,&attr);err!=nil{
		return err
	}
	attrMap[attr.MetaDataItem.Name]=attr.MetaDataItem
	return nil
}

func readApi(f *os.File)error{
	var data gs.MapData
	content, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}
	if err :=yaml.Unmarshal(content, &data);err!=nil{
		return err
	}
	service :=data.String("metadata.service")
	paths:=data.String("metadata.path")
	endpoints,_:=data.FindField("metadata.endpoints")
	endpointsMap :=endpoints.([]interface{})
	for _,p:=range endpointsMap{
		mapP:=p.(map[interface{}]interface{})
		method :=mapP["method"].(string)
		httpMethod,route :=mapFirstKv(mapP["route"].(map[interface{}]interface{}))
		apiItem:=ApiItem{
			Method:common.CamelCase(service,true)+common.CamelCase(method,true),
			Path: paths+route,
			HttpMethod:strings.ToUpper(httpMethod),
		}
		apiItem.Path = strings.Replace(apiItem.Path,"}","",-1)
		apiItem.Path = strings.Replace(apiItem.Path,"/{","/:",-1)
		apiMap[apiItem.Method] = apiItem
		apiKey = append(apiKey,apiItem.Method)
	}
	return nil
}

func wrapperWalkDirFunc(doFile func(f *os.File)error)fs.WalkDirFunc{
	return func(path string, d fs.DirEntry, err error)error{
		defer func(){
			if p:=recover();p!=nil{
				fmt.Println(path)
			}
		}()
		if d==nil{
			return nil
		}
		if d.IsDir(){
			return nil
		}
		if f,err:=os.Open(path);err!=nil{
			return err
		}else {
			err=doFile(f)
		}
		return err
	}
}

func mapFirstOne(mts map[string]interface{})string{
	for k,v:=range mts{
		value:=common.AssertString(v)
		if strings.ToUpper(k)=="ARRAY"{
			return "[]"+value
		}
		return value
	}
	return ""
}

func mapFirstKv(mts map[interface{}]interface{})(string,string){
	for k,v:=range mts{
		return k.(string),v.(string)
	}
	return "",""
}

func printSchema(){
	var keys []string
	for k,_:=range schemaMap{
		keys = append(keys,k)
	}
	sort.Strings(keys)


	for _,k :=range keys{
		schema:=schemaMap[k]
		fmt.Println("\n\n")
		fmt.Printf("%v	%v\n\n",schema.MetaData.Name,schema.MetaData.Description)
		for _,v :=range schema.MetaData.Attributes{
			if len(v.Ref)>0{
				if attr,ok:=attrMap[v.Ref];ok{
					fmt.Printf("%-15s %-10s %v\n",attr.Name,mapFirstOne(attr.Type),attr.Description)
				}
				continue
			}
			fmt.Printf("%-15s %-10s %v\n",v.Name,mapFirstOne(v.Type),v.Description)
		}
	}
}

func printGateWay(){
	var keys []string
	for k,_:=range apiMap{
		keys = append(keys,k)
	}
	sort.Strings(keys)

	var apiList []ApiItem
	for _,k:=range keys{
		apiList = append(apiList,apiMap[k])
	}

	t:=template.New("gateway")
	gt,err:= t.Parse(gatewayTemplate)
	if err!=nil{
		fmt.Println(err)
	}
	buf :=bytes.NewBuffer(nil)
	gt.Execute(buf,map[string]interface{}{"Routers":apiList})
	fmt.Println("print gateway")
	fmt.Println(buf.String())
}