// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package cmd

import (
	"context"

	"github.com/gogf/gf/v2/os/gproc"
	"github.com/gogf/gf/v2/text/gregex"

	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gfile"
	"github.com/gogf/gf/v2/text/gstr"
	"hotgo/internal/library/hggen/internal/utility/mlog"
)

var (
	Fix = cFix{}
)

type cFix struct {
	g.Meta `name:"fix" brief:"auto fixing codes after upgrading to new GoFrame version" usage:"gf fix" `
}

type cFixInput struct {
	g.Meta  `name:"fix"`
	Path    string `name:"path"    short:"p" brief:"directory path, it uses current working directory in default"`
	Version string `name:"version" short:"v" brief:"custom specified version to fix, leave it empty to auto detect"`
}

type cFixOutput struct{}

type cFixItem struct {
	Version string
	Func    func(version string) error
}

func (c cFix) Index(ctx context.Context, in cFixInput) (out *cFixOutput, err error) {

	if in.Path == "" {
		in.Path = gfile.Pwd()
	}
	if in.Version == "" {
		in.Version, err = c.autoDetectVersion(in)
		if err != nil {
			mlog.Fatal(err)
		}
		if in.Version == "" {
			mlog.Print(`no GoFrame usage found, exit fixing`)
			return
		}
		mlog.Debugf(`current GoFrame version auto detect "%s"`, in.Version)
	}

	if !gproc.IsChild() {
		mlog.Printf(`start auto fixing directory path "%s"...`, in.Path)
		defer mlog.Print(`done!`)
	}

	err = c.doFix(in)
	return
}

func (c cFix) doFix(in cFixInput) (err error) {

	var items = []cFixItem{
		{Version: "v2.3", Func: c.doFixV23},
		{Version: "v2.5", Func: c.doFixV25},
	}
	for _, item := range items {
		if gstr.CompareVersionGo(in.Version, item.Version) < 0 {
			mlog.Debugf(
				`current GoFrame or contrib package version "%s" is lesser than "%s", nothing to do`,
				in.Version, item.Version,
			)
			continue
		}
		if err = item.Func(in.Version); err != nil {
			return
		}
	}
	return
}

// doFixV23 fixes code when upgrading to GoFrame v2.3.
func (c cFix) doFixV23(version string) error {
	replaceFunc := func(path, content string) string {
		// gdb.TX from struct to interface.
		content = gstr.Replace(content, "*gdb.TX", "gdb.TX")
		// function name changes for package gtcp/gudp.
		if gstr.Contains(content, "/gf/v2/net/gtcp") || gstr.Contains(content, "/gf/v2/net/gudp") {
			content = gstr.ReplaceByMap(content, g.MapStrStr{
				".SetSendDeadline":      ".SetDeadlineSend",
				".SetReceiveDeadline":   ".SetDeadlineRecv",
				".SetReceiveBufferWait": ".SetBufferWaitRecv",
			})
		}
		return content
	}
	return gfile.ReplaceDirFunc(replaceFunc, ".", "*.go", true)
}

// doFixV25 fixes code when upgrading to GoFrame v2.5.
func (c cFix) doFixV25(version string) (err error) {
	replaceFunc := func(path, content string) string {
		content, err = c.doFixV25Content(content)
		return content
	}
	return gfile.ReplaceDirFunc(replaceFunc, ".", "*.go", true)
}

func (c cFix) doFixV25Content(content string) (newContent string, err error) {
	newContent = content
	if gstr.Contains(content, `.BindHookHandlerByMap(`) {
		var pattern = `\.BindHookHandlerByMap\((.+?), map\[string\]ghttp\.HandlerFunc`
		newContent, err = gregex.ReplaceString(
			pattern,
			`.BindHookHandlerByMap($1, map[ghttp.HookName]ghttp.HandlerFunc`,
			content,
		)
	}
	return
}

func (c cFix) autoDetectVersion(in cFixInput) (string, error) {
	var (
		err     error
		path    = gfile.Join(in.Path, "go.mod")
		version string
	)
	if !gfile.Exists(path) {
		return "", gerror.Newf(`"%s" not found in current working directory`, path)
	}
	err = gfile.ReadLines(path, func(line string) error {
		array := gstr.SplitAndTrim(line, " ")
		if len(array) > 0 {
			if gstr.HasPrefix(array[0], gfPackage) {
				version = array[1]
			}
		}
		return nil
	})
	if err != nil {
		mlog.Fatal(err)
	}
	return version, nil
}
