package main

import (
	"regexp"

	parser "github.com/yoheimuta/go-protoparser/v4/parser"
)

type Transformer struct {
}

var PartStr2Enum = map[string]Part{
	"params": Params,
	"body":   Body,
	"data":   Data,
	"res":    Res,
}

func (t *Transformer) transform(pkg, nested string, protoBody []parser.Visitee) (apis *map[string]map[Part]*ApiPart, segments *map[string]*Segment, err error) {
	pkgName := pkg

	a := make(map[string]map[Part]*ApiPart)
	apis = &a

	s := make(map[string]*Segment)
	segments = &s

	for _, it := range protoBody {
		switch v := it.(type) {
		case *parser.Package:
			pkgName = v.Name
		case *parser.Enum:
			pbType := completePbType(pkgName, nested, v.EnumName)
			(*segments)[pbType] = &Segment{
				pbPkg:  pkgName,
				pbType: pbType,
				pbEnum: v,
			}
		case *parser.Message:
			// hanlde segment
			pbType := completePbType(pkgName, nested, v.MessageName)
			(*segments)[pbType] = &Segment{
				pbPkg:     pkgName,
				pbType:    pbType,
				pbMessage: v,
			}
			// handle api comment
			for _, comt := range v.Comments {
				apiName, apiPart := matchApiLabel(comt.Raw)
				if apiName != "" && apiPart != "" {
					if _, ok := (*apis)[apiName]; !ok {
						(*apis)[apiName] = make(map[Part]*ApiPart)
					}
					if v, ok := PartStr2Enum[apiPart]; ok {
						(*apis)[apiName][v] = &ApiPart{
							apiName: apiName,
							part:    v,
							pbType:  pbType,
						}
					}
				}
			}
			// handle nested
			nNested := v.MessageName
			if nested != "" {
				nNested = nested + "." + v.MessageName
			}
			napis, nsegments, err := t.transform(pkgName, nNested, v.MessageBody)
			if err != nil {
				return napis, nsegments, err
			}

			mergeApis(apis, napis)
			mergeSegments(segments, nsegments)
		}
	}

	return
}

func completePbType(pkg, nested, name string) string {
	if nested != "" {
		return pkg + "." + nested + "." + name
	}
	return pkg + "." + name
}

func matchApiLabel(comt string) (apiName string, part string) {
	var indepComtMatchRegex = regexp.MustCompile(`^/\*[^@]*@api\s*:\s*(\w+)\s*:\s*(params|body|data|res).*\*/$`)
	var simpleComtMatchRegex = regexp.MustCompile(`^//[^@]*@api\s*:\s*(\w+)\s*:\s*(params|body|data|res).*$`)

	indepRes := indepComtMatchRegex.FindStringSubmatch(comt)
	if len(indepRes) > 2 {
		return indepRes[1], indepRes[2]
	}

	simpleRes := simpleComtMatchRegex.FindStringSubmatch(comt)
	if len(simpleRes) > 2 {
		return simpleRes[1], simpleRes[2]
	}

	return
}
