package gogen

import (
	"fmt"
	"go/ast"
	"strings"

	"github.com/gobuffalo/genny"
	"github.com/pkg/errors"
)

// AddInsideBlock will add anything inside of the app declaration block inside of file
func AddInsideBlock(gf genny.File, search string, expressions ...string) (genny.File, error) {
	pf, err := ParseFile(gf)
	if err != nil {
		return gf, errors.WithStack(err)
	}
	gf = pf.File

	_, end := findBlockCoordinates(search, pf)
	if end < 0 {
		return gf, errors.Errorf("could not find desired block in %s", gf.Name())
	}

	if len(pf.Lines) == end {
		end = end - 1
	}

	el := pf.Lines[end:]
	sl := []string{}
	sf := []string{}
	for _, l := range pf.Lines[:end] {
		// if there's a app.ServeFiles("/", foo) line it needs to be the last added to the router
		if strings.Contains(l, "ServeFiles(\"/\"") {
			sf = append(sf, l)
			continue
		}
		sl = append(sl, l)
	}

	for i := 0; i < len(expressions); i++ {
		expressions[i] = fmt.Sprintf("\t\t%s", expressions[i])
	}

	el = append(sf, el...)
	pf.Lines = append(sl, append(expressions, el...)...)

	fileContent := strings.Join(pf.Lines, "\n")
	return genny.NewFile(gf.Name(), strings.NewReader(fileContent)), nil
}

func findBlockCoordinates(search string, pf ParsedFile) (int, int) {
	var end = -1
	var start = -1

	ast.Inspect(pf.Ast, func(n ast.Node) bool {
		switch x := n.(type) {
		case *ast.StructType:
			line := pf.FileSet.Position(x.Pos()).Line
			structDeclaration := fmt.Sprintf("%s\n", pf.Lines[line-1])

			if strings.Contains(structDeclaration, search) {
				start = line
				end = pf.FileSet.Position(x.End()).Line
				return false
			}

		case *ast.BlockStmt:
			start = pf.FileSet.Position(x.Lbrace).Line
			blockDeclaration := fmt.Sprintf("%s\n", pf.Lines[start-1])

			if strings.Contains(blockDeclaration, search) {
				end = pf.FileSet.Position(x.Rbrace).Line - 1
			}

		}
		return true
	})

	return start, end
}
