package syw

import (
	"context"
	"text/template"

	"tildegit.org/tjp/sliderule"
	"tildegit.org/tjp/sliderule/gemini"
)

// GeminiRouter builds a router that will handle requests into a directory of git repositories.
//
// The routes it defines are:
//
//	/                                     gemtext listing of the repositories in the directory
//	/:repository/                         gemtext overview of the repository
//	/:repository/branches                 gemtext list of branches/heads
//	/:repository/tags                     gemtext listing of tags
//	/:repository/refs/:ref/               gemtext overview of a ref
//	/:repository/refs/:ref/tree/*path     gemtext listing of directories, raw files
//	/:repository/diffstat/:fromref/:toref text/plain diffstat between two refs
//	/:repository/diff/:fromref/:toref     text/x-diff between two refs
//
// The overrides argument can provide templates to define the behavior of nearly all of the above
// routes. All of them have default implementations so the argument can even be nil, but otherwise
// the template names used are:
//
//	repo_root.gmi    gemtext at /
//	repo_home.gmi    gemtext at /:repository/
//	branch_list.gmi  gemtext at /:repository/branches
//	tag_list.gmi     gemtext at /:repository/tags
//	ref.gmi          gemtext at /:repository/refs/:ref/
//	tree.gmi         gemtext for directories requested under /:repository/refs/:ref/tree/*path
//	                 (file paths return the raw files without any template involved)
//	diffstat.gmi.txt the plaintext diffstat at /:repository/diffstat/:fromref/:toref
//	diff.gmi.txt     the text/x-diff at /:repository/diff/:fromref/:toref
//
// Most of the templates above are rendered with an object with 3 fields:
//
//	Ctx:    the context.Context from the request
//	Repo:   a *syw.Repository object corresponding to <repodir>/:repository
//	Params: a map[string]string of the route parameters
//
// The only exception is repo_root.gmi, which is rendered with an object containing a single name
// "Repos", which is a slice of the repository names.
func GeminiRouter(repodir string, overrides *template.Template) *sliderule.Router {
	tmpl, err := addTemplates(geminiTemplate, overrides)
	if err != nil {
		panic(err)
	}

	repoRouter := &sliderule.Router{}
	repoRouter.Use(assignRepo(repodir))
	repoRouter.Route("/", repoRouteHandler(geminiProto, tmpl, "repo_home.gmi"))
	repoRouter.Route("/branches", repoRouteHandler(geminiProto, tmpl, "branch_list.gmi"))
	repoRouter.Route("/tags", repoRouteHandler(geminiProto, tmpl, "tag_list.gmi"))
	repoRouter.Route("/refs/:ref/", repoRouteHandler(geminiProto, tmpl, "ref.gmi"))
	repoRouter.Route("/refs/:ref/tree/*path", treePathHandler(geminiProto, tmpl, "tree.gmi"))
	repoRouter.Route("/diffstat/:fromref/:toref", repoRouteHandler(geminiProto, tmpl, "diffstat.gmi.txt"))
	repoRouter.Route("/diff/:fromref/:toref", repoRouteHandler(geminiProto, tmpl, "diff.gmi.txt"))

	router := &sliderule.Router{}
	router.Route("/", rootDirHandler(geminiProto, repodir, tmpl, "repo_root.gmi"))
	router.Mount("/:"+reponamekey, repoRouter)

	return router
}

type geminiProtocol struct{ sliderule.ServerProtocol }

func (geminiProtocol) TemplateBaseData(_ context.Context, _ *sliderule.Request) map[string]any {
	return map[string]any{}
}

func (geminiProtocol) TemplateRepoData(ctx context.Context, request *sliderule.Request) map[string]any {
	return map[string]any{
		"Ctx":    ctx,
		"Repo":   ctx.Value(repokey),
		"Params": sliderule.RouteParams(ctx),
	}
}

var geminiProto = geminiProtocol{gemini.ServerProtocol}