package generator

import (
	"io"
	"strings"

	"github.com/dave/jennifer/jen"
	"go.elara.ws/go-lemmy/cmd/gen/extractor"
)

type RoutesGenerator struct {
	w       io.Writer
	PkgName string
}

func NewRoutes(w io.Writer, pkgName string) *RoutesGenerator {
	return &RoutesGenerator{w, pkgName}
}

func (r *RoutesGenerator) Generate(routes []extractor.Route) error {
	f := jen.NewFile(r.PkgName)
	f.HeaderComment("Code generated by go.elara.ws/go-lemmy/cmd/gen (routes generator). DO NOT EDIT.")

	for _, r := range routes {
		f.Comment(r.Summary)
		f.Func().Params(
			jen.Id("c").Id("*Client"),
		).Id(transformName(r.Name)).ParamsFunc(func(g *jen.Group) {
			g.Id("ctx").Qual("context", "Context")
			if r.ParamsName != "" {
				g.Id("data").Id(r.ParamsName)
			}
		}).ParamsFunc(func(g *jen.Group) {
			if r.ReturnName == "map[string]any" {
				g.Map(jen.String()).Any()
			} else if r.ReturnName != "" {
				g.Op("*").Id(r.ReturnName)
			}

			g.Error()
		}).BlockFunc(func(g *jen.Group) {
			data := jen.Id("data")
			// If there are no parameters, set the data to nil
			if r.ParamsName == "" {
				data = jen.Nil()
			}

			returnName := r.ReturnName
			if returnName == "" {
				returnName = "emptyResponse"
			}

			if returnName == "map[string]any" {
				g.Id("resData").Op(":=").Map(jen.String()).Any().Block()
			} else {
				g.Id("resData").Op(":=").Op("&").Id(returnName).Block()
			}

			funcName := "req"
			if r.Method == "GET" {
				funcName = "getReq"
			}

			g.List(jen.Id("res"), jen.Err()).Op(":=").Id("c").Dot(funcName).ParamsFunc(func(g *jen.Group) {
				g.Id("ctx")
				g.Lit(r.Method)
				g.Lit(r.Path)
				g.Add(data)
				
				if returnName == "map[string]any" {
					g.Op("&").Id("resData")
				} else {
					g.Id("resData")
				}
			})
			g.If(jen.Err().Op("!=").Nil()).BlockFunc(func(g *jen.Group) {
				if returnName == "emptyResponse" {
					g.Return(jen.Err())
				} else {
					g.Return(jen.Nil(), jen.Err())
				}
			})

			if r.ReturnName == "map[string]any" {
				g.Err().Op("=").Id("resError").Params(jen.Id("res"), jen.Id("NewOptionalNil[string]").Params())
			} else {
				g.Err().Op("=").Id("resError").Params(jen.Id("res"), jen.Id("resData").Dot("Error"))
			}
			
			g.If(jen.Err().Op("!=").Nil()).BlockFunc(func(g *jen.Group) {
				if returnName == "emptyResponse" {
					g.Return(jen.Err())
				} else {
					g.Return(jen.Nil(), jen.Err())
				}
			})

			if returnName == "emptyResponse" {
				g.Return(jen.Nil())
			} else {
				g.Return(jen.Id("resData"), jen.Nil())
			}
		})
	}

	return f.Render(r.w)
}

func transformName(s string) string {
	s = strings.ToUpper(s[:1]) + s[1:]
	s = strings.TrimPrefix(s, "Get")
	s = strings.TrimPrefix(s, "List")
	return s
}
