package wp

import (
	"github.com/fthvgb1/wp-go/app/cmd/reload"
	"github.com/fthvgb1/wp-go/app/pkg/config"
	"github.com/fthvgb1/wp-go/app/pkg/constraints"
	"github.com/fthvgb1/wp-go/app/pkg/logs"
	"github.com/fthvgb1/wp-go/app/plugins/wphandle/apply"
	"github.com/fthvgb1/wp-go/app/wpconfig"
	"github.com/fthvgb1/wp-go/helper/maps"
	str "github.com/fthvgb1/wp-go/helper/strings"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"html/template"
	"net/http"
	"os"
)

type Handle struct {
	Index             *IndexHandle
	Detail            *DetailHandle
	C                 *gin.Context
	theme             string
	Session           sessions.Session
	ginH              gin.H
	password          string
	scene             string
	Code              int
	Stats             string
	templ             string
	components        map[string]map[string][]Components[string]
	componentHook     map[string][]func(Components[string]) (Components[string], bool)
	themeMods         wpconfig.ThemeMods
	handlers          map[string]map[string][]HandleCall
	handleHook        map[string][]func(HandleCall) (HandleCall, bool)
	err               error
	abort             bool
	stopPipe          bool
	componentsArgs    map[string]any
	componentFilterFn map[string][]func(*Handle, string, ...any) string
	template          *template.Template
}

func (h *Handle) GinH() gin.H {
	return h.ginH
}

func (h *Handle) SetScene(scene string) {
	h.scene = scene
}

func (h *Handle) Components() map[string]map[string][]Components[string] {
	return h.components
}

func (h *Handle) ComponentHook() map[string][]func(Components[string]) (Components[string], bool) {
	return h.componentHook
}

func (h *Handle) Handlers() map[string]map[string][]HandleCall {
	return h.handlers
}

func (h *Handle) HandleHook() map[string][]func(HandleCall) (HandleCall, bool) {
	return h.handleHook
}

func (h *Handle) SetTemplate(template *template.Template) {
	h.template = template
}

func (h *Handle) Template() *template.Template {
	return h.template
}

type HandlePlugins map[string]HandleFn[*Handle]

// Components Order 为执行顺序，降序执行
type Components[T any] struct {
	Name   string
	Val    T
	Fn     func(*Handle) T
	Order  float64
	Cached bool
}

type HandleFn[T any] func(T)

type HandleCall struct {
	Fn    HandleFn[*Handle]
	Order float64
	Name  string
}

func InitHandle(fn func(*Handle), h *Handle) {
	var inited = false
	hh := reload.GetAnyValBys("themeArgAndConfig", h, func(h *Handle) Handle {
		h.components = make(map[string]map[string][]Components[string])
		h.componentsArgs = make(map[string]any)
		h.componentFilterFn = make(map[string][]func(*Handle, string, ...any) string)
		h.handlers = make(map[string]map[string][]HandleCall)
		h.handleHook = make(map[string][]func(HandleCall) (HandleCall, bool))
		h.ginH = gin.H{}
		fnMap = map[string]map[string]any{}
		fnHook = map[string]map[string]any{}
		fn(h)
		v := apply.UsePlugins()
		pluginFn, ok := v.(func(*Handle))
		if ok {
			pluginFn(h)
		}
		h.C.Set("inited", true)
		inited = true
		return *h
	})
	h.ginH = maps.Copy(hh.ginH)
	h.ginH["calPostClass"] = postClass(h)
	h.ginH["calBodyClass"] = bodyClass(h)
	h.ginH["customLogo"] = customLogo(h)
	if inited {
		return
	}
	h.components = hh.components
	h.Index.postsPlugin = hh.Index.postsPlugin
	h.Index.pageEle = hh.Index.pageEle
	h.Detail.CommentRender = hh.Detail.CommentRender
	h.handlers = hh.handlers
	h.handleHook = hh.handleHook
	h.componentHook = hh.componentHook
	h.componentsArgs = hh.componentsArgs
	h.componentFilterFn = hh.componentFilterFn
	h.C.Set("inited", true)
}

func (h *Handle) Abort() {
	h.abort = true
	h.stopPipe = true
}
func (h *Handle) StopPipe() {
	h.stopPipe = true
}
func (h *Handle) StopHandle() {
	h.abort = true
}

func (h *Handle) CommonThemeMods() wpconfig.ThemeMods {
	return h.themeMods
}

func (h *Handle) Err() error {
	return h.err
}

func (h *Handle) SetErr(err error) {
	h.err = err
}

func (h *Handle) SetTempl(templ string) {
	h.templ = templ
}

func (h *Handle) Scene() string {
	return h.scene
}

func (h *Handle) SetDatas(GinH gin.H) {
	maps.Merge(h.ginH, GinH)
}
func (h *Handle) SetData(k string, v any) {
	h.ginH[k] = v
}

func NewHandle(c *gin.Context, scene string, theme string) *Handle {
	mods, err := wpconfig.GetThemeMods(theme)
	logs.IfError(err, "获取mods失败")
	return &Handle{
		C:         c,
		theme:     theme,
		Session:   sessions.Default(c),
		scene:     scene,
		Stats:     constraints.Ok,
		themeMods: mods,
	}
}

func (h *Handle) GetPassword() string {
	if h.password != "" {
		return h.password
	}
	pw := h.Session.Get("post_password")
	if pw != nil {
		h.password = pw.(string)
	}
	return h.password
}

func PreTemplate(h *Handle) {
	if h.templ == "" {
		h.templ = str.Join(h.theme, "/posts/index.gohtml")
		if h.scene == constraints.Detail {
			h.templ = str.Join(h.theme, "/posts/detail.gohtml")
		}
	}
}
func PreCodeAndStats(h *Handle) {
	if h.Stats != "" && h.Code != 0 {
		return
	}
	switch h.Stats {
	case constraints.Ok:
		h.Code = http.StatusOK
	case constraints.ParamError, constraints.Error404:
		h.Code = http.StatusNotFound
	case constraints.InternalErr:
		h.Code = http.StatusInternalServerError
	}
}

var htmlContentType = []string{"text/html; charset=utf-8"}

func (h *Handle) RenderHtml(t *template.Template, statsCode int, name string) {
	header := h.C.Writer.Header()
	if val := header["Content-Type"]; len(val) == 0 {
		header["Content-Type"] = htmlContentType
	}
	h.C.Status(statsCode)
	err := t.ExecuteTemplate(h.C.Writer, name, h.ginH)
	h.Abort()
	if err != nil {
		panic(err)
	}
}

func (h *Handle) PushHandlers(pipeScene string, call HandleCall, statsOrScene ...string) {
	for _, s := range statsOrScene {
		h.PushHandler(pipeScene, s, call)
	}
}

func (h *Handle) CommonComponents() {
	h.PushCacheGroupHeadScript(constraints.AllScene, "siteIconAndCustomCss", 0, CalSiteIcon, CalCustomCss)
	h.PushRender(constraints.AllStats, NewHandleFn(CalComponents, 10.001, "wp.CalComponents"))
	h.PushRender(constraints.AllStats, NewHandleFn(PreRenderTemplate, 0, "wp.PreRenderTemplate"))
	AdditionScript(h)
}

func AdditionScript(h *Handle) {
	s := config.GetConfig().ExternScript
	if len(s) < 1 {
		return
	}
	fn := func(f, name string) {
		if f == "" {
			return
		}
		ss, err := os.ReadFile(f)
		if err != nil {
			logs.Error(err, str.Join("解析", name, "失败"), f)
		} else {
			h.PushComponents(constraints.AllScene, constraints.HeadScript, NewComponent(name, string(ss), false, 0, nil))
		}
	}
	switch len(s) {
	case 1:
		fn(s[0], "externHead")
	case 2:
		fn(s[0], "externHead")
		fn(s[1], "externFooter")
	}
}

func PreRenderTemplate(h *Handle) {
	h.C.HTML(h.Code, h.templ, h.ginH)
	h.Abort()
}

func NewHandleFn(fn HandleFn[*Handle], order float64, name string) HandleCall {
	return HandleCall{Fn: fn, Order: order, Name: name}
}

func NothingToDo(h *Handle) {
	h.Abort()
}
