package got

import (
	"text/template"

	. "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/common"
	G "github.com/dave/jennifer/jen"
)

const ()

var (
	deleteStmtsTmpl, hasDepDeleteStmtsTmpl *template.Template
	deleteStmtsErr                         error
)

func init() {

	deleteStmtsTmpl, deleteStmtsErr = ParseTemplate(`
		var (
			values = ctx.Values()
		)
		values.Set("hard.deletion", {{.hardDeletion}})

		filter := values.Get("$filter").(*api.Filter)
		filter.DB = "{{.dbName}}"
		filter.Collection = "{{.collectionName}}"
			
		{{if .createDepFunc}}
		if HasDep{{.entity}}(filter) {
			err = errs.FailedPrecondition().Details(&errs.Detail{
				Message: "Ocorreu um conflito ao remover a entidade '{{.entity}}'",
			})
			return
		}
		{{end}}


		{{if .preconditions}}
			if _, err = executeAction(
				ctx,
				{{.preconditions}},
			); err != nil {
				return
			}
		{{end}}
		
		if hardDeletion, _ := values.GetBool("hard.deletion"); hardDeletion {
			_, err = models.Api.RemoveOne(filter)
		} else {
			if replacement := values.Get("replace.default.patch"); replacement != nil  {
				filter.Patchs = replacement.(*bson.A)
			} else {
				filter.Patchs = api.DeletedPatch()
			}
			_, err = models.Api.PatchOne(filter)
		}

		if err != nil {
			err.Details(&errs.Detail{
				Dominio:      "",
				Reason:       "",
				Location:     "middleware.path",
				LocationType: "middleware.operation",
			})
		}

		{{if .beforeResponse}}
		if resp, err = executeAction(
			ctx,
			{{.beforeResponse}},
		); err != nil || resp != nil {
			return
		}
		{{end}}

		resp = ""
		
		return`)

	if deleteStmtsErr != nil {
		panic(deleteStmtsErr)
	}

	hasDepDeleteStmtsTmpl, deleteStmtsErr = ParseTemplate(`	
	//  {{.dependenceMethod}} verifica se a entidade '{{.entity}}' possui dependencias com outras entidades.
	func {{.dependenceMethod}}(options *api.Filter) bool {
		var (
			foundChan = make(chan bool)
			find = func(db, collection, attr string) {
				exists, _ := models.Api.Exists(&api.Filter{
					DB:         db,
					Collection: collection,
					Query:      &bson.M{attr: options.Id},
				})
				foundChan <- exists
			}
		)
		
	
		{{range .relations}}
			go find("{{.DB}}", "{{.Collection}}", "{{.Attr}}._id")
		{{end}}
	
		for i := 0; i < {{.relationCount}}; i++ {
			select {
				{{range $idx, $relation := .relations}} case x{{$idx}} := <-foundChan:
				if x{{$idx}} {
					return true
				}
				{{end}}
			}
		}
	
		return false
	}`)

	if deleteStmtsErr != nil {
		panic(deleteStmtsErr)
	}

}

var (
	GenDeleteStmts = &Middleware{
		Id:   "delete",
		Type: "method",
		Fn: func(ctx *MiddlewareContext) error {
			var (
				project          = ctx.Project
				method           = ctx.Method
				dbName           = project.GetEntityDB(method.Entity)
				collectionName   = project.GetCollection(method.Entity)
				relations        = SR.Get(method.Entity)
				relationCount    = len(relations)
				dependenceMethod = BASE_HAS_DEPENDE + method.Entity
				createDepFunc    = SR.Has(method.Entity)
				beforeSend       = method.Hook("beforeSend")
				beforePersist    = method.Hook("beforePersist")
				context          = map[string]interface{}{
					"dbName":           dbName,
					"collectionName":   collectionName,
					"entity":           method.Entity,
					"createDepFunc":    createDepFunc,
					"relationCount":    relationCount,
					"relations":        relations,
					"dependenceMethod": dependenceMethod,
					"beforePersist":    beforePersist,
					"beforeSend":       beforeSend,
					"hardDeletion":     getCustom(method.Custom, "go.entiy.hard.deletion", false),
					"preconditions":    parseMethodActions(method.Preconditions),
					"beforeResponse":   parseMethodActions(method.BeforeResponse),
				}
			)
			// Nome do metodo que verifica se a entidade tem dependencias

			out, _ := TemplateToString(deleteStmtsTmpl, context)
			afterMethod := ctx.Statement.Block(G.Id(out)).Line()

			if createDepFunc {
				out, _ = TemplateToString(hasDepDeleteStmtsTmpl, context)
				afterMethod.Id(out)
			}

			return nil
		},
	}
)

// afterDefineDelete.Do(func(x *G.Statement) {

// 	x.Add(G.Comment(
// 		fmt.Sprintf("%s verifica se a entidade '%s' possui dependencias com outras entidades.", dependenceMethod, method.Entity),
// 	).Line())

// 	// Cria as variaveis

// 	block := G.Func().Id(dependenceMethod).Params(
// 		G.Id("f").Op("*").Qual(API_URL, "Filter"),
// 	).Bool().BlockFunc(func(b *G.Group) {

// 		for index, relation := range relations {
// 			// for index, _ := range relations {
// 			indexStr = strconv.Itoa(index)
// 			cIndex = "c" + indexStr
// 			xIndex = "x" + indexStr
// 			// Cria o canal para a variavel
// 			vars = append(vars, G.Id(cIndex).Op("=").Id("make").Call(G.Chan().Bool()))

// 			// Define um case para um canal
// 			cases = append(cases, G.Case(G.Id(xIndex).Op(":=").Op("<-").Id("c"+indexStr)).Block(
// 				G.If(G.Id(xIndex)).Block(G.Return(G.True())),
// 			))

// 			rotines = append(rotines, G.Go().Func().Params().Block(
// 				G.Id("filter").Op(":=").Op("&").Qual(API_URL, "Filter").Values(G.Dict{
// 					// Especifica a collection
// 					G.Id("DB"):         G.Lit(relation.DB),
// 					G.Id("Collection"): G.Lit(relation.Collection),

// 					// Especifica a query
// 					G.Id("Query"): G.Op("&").Qual(BSON, "M").Values(G.Dict{
// 						G.Lit(relation.Attr + "._id"): G.Id("f").Dot("Id"),
// 					}),
// 				}),

// 				G.Id(cIndex).Op("<-").Id("Models").Dot("Exist").Call(G.Id("filter")),
// 			).Call().Line())
// 		}

// 		// Adiciona as variaveis de canal
// 		b.Add(G.Var().DefsFunc(func(b2 *G.Group) {
// 			// Gera o case para cada relacao
// 			for _, v := range vars {
// 				b2.Add(v)
// 			}
// 		}))

// 		// Adiciona as chamadas das rotinas que consultam a existencia de dependencia
// 		for _, r := range rotines {
// 			// fmt.Printf("%#v", r)
// 			b.Add(r)
// 		}

// 		// Geracao do for select
// 		b.Add(G.For(
// 			G.Id("i").Op(":=").Lit(0),
// 			G.Id("i").Op("<").Lit(relationCount),
// 			G.Id("i").Op("++"),
// 		).Block(G.Select().BlockFunc(func(b1 *G.Group) {
// 			// Gera o case para cada relacao
// 			for _, case1 := range cases {
// 				b1.Add(case1)
// 			}

// 		})).Line())

// 		b.Return(G.False())
// 		// fmt.Printf("%#v")

// 	})

// 	x.Add(block)

// }).Line()

// if  {
// 				createDepFunc = true

// 				do.Add(G.If(G.Id(dependenceMethod).Call(
// 					G.Id("filter"),
// 				)).Block(
// 					G.Id("err").Op("=").Qual(API_URL, "Error").Call(
// 						G.Qual(API_URL, "ERR_CONFLICT"),
// 						G.Lit(fmt.Sprintf("Ocorreu um conflito ao remover a entidade '%s'", method.Entity)),
// 					),
// 					G.Return(),
// 				).Line())
// 			}

// ctx.Statement.Block(
// 	G.Id(`filter := ctx.Values.Get("$filter")`).Assert(G.Op("*").Qual(API_URL, "Filter")).Line(),
// 	G.Id("filter").Dot("DB").Op("=").Lit(dbName),
// 	G.Id("filter").Dot("Collection").Op("=").Lit(collectionName).Line(),

// 	// Verifica se o tem tem dependencias
// 	G.Do(func(do *G.Statement) {

// 		if SR.Has(method.Entity) {
// 			createDepFunc = true

// 			do.Add(G.If(G.Id(dependenceMethod).Call(
// 				G.Id("filter"),
// 			)).Block(
// 				G.Id("err").Op("=").Qual(API_URL, "Error").Call(
// 					G.Qual(API_URL, "ERR_CONFLICT"),
// 					G.Lit(fmt.Sprintf("Ocorreu um conflito ao remover a entidade '%s'", method.Entity)),
// 				),
// 				G.Return(),
// 			).Line())
// 		}
// 		// fmt.Println("GenerateFunc" + method.Entity + "|" + dependenceMethod)
// 	}),

// 	G.Do(func(s *G.Statement) {
// 		if method.BeforePersistAction {
// 			fnName := fmt.Sprintf("Before%s%s", strings.Title(method.ID), method.Entity)
// 			s.Add(G.Id(fmt.Sprintf(`
// 			 // Chama uma função onde são descritas as ações executadas antes da entidade ser persistida.
// 			 if err = %s(ctx, resp); err != nil {
// 				return
// 			 }
// 			`, fnName)))

// 			// part := s.Comment("").Line()
// 			// part.If(
// 			// 	G.List(G.Id("fn"), G.Id("err")).Op("=").Id(fnName).Call(G.Id("ctx"), G.Id("patchs")),
// 			// 	G.Id("err").Op("!=").Id("nil"),
// 			// ).Block(G.Return())
// 		}
// 	}),

// 	// Executa a remocao do item
// 	// G.If(
// 	// G.List(G.Id("_"), G.Id("err")).Op("=").Id("Models").Dot("RemoveOne").Call(
// 	// 	G.Id("filter"),
// 	// ),

// 	G.Id(`
// 	filter.Patchs = &`).Qual(BSON, "A").Id(`{
// 		bson.M{
// 			"$set": bson.M{
// 				"deleted":   true,
// 				"deletedIn": `).Qual("time", "Now").Id(`().Unix(),
// 			},
// 		},
// 	}`),
// 	generateHookCall(project, method, "beforePersist"),
// 	G.Id(`if _, err = models.Api.PatchOne(filter), err !`),

// 	G.Return(),
// 	// G.Return(G.Nil()),
// ).
// --------------------------------------------------
// G.Id("err").Op("!=").Id("nil"),
// ).Block(
// G.Return(G.Id("err")),
// G.Return(),
// ),
// G.Var().Defs(
// 	G.Id("err").Op("*").Qual(API_ERROR, "Error"),
// 	// G.Id("entity").Op("=").Op("&").Id(method.Entity).Values(),
// ).Line(),

// fmt.Println("Compont:" + method.Entity)

// block.Add(
// 	x.Var().Defs(
// 		G.Id("err").Op("*").Qual(API_ERROR, "Error"),
// 		G.Id("entity").Op("=").Op("&").Id(method.Entity).Values(),
// 	),
// )

// vars.Defs(varList...)
