package pongo

import (
	"fmt"
	"net/http"
	"sync"

	"github.com/flosch/pongo2/v6"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/render"
	"github.com/sirupsen/logrus"
)

type Renderer struct {
	mu            sync.RWMutex
	globalContext pongo2.Context
	tplSet        *pongo2.TemplateSet
	logger        *logrus.Logger
}

func New(directory string, isDebug bool, logger *logrus.Logger) *Renderer {
	loader := pongo2.MustNewLocalFileSystemLoader(directory)
	tplSet := pongo2.NewSet("gin-pongo-templates", loader)
	tplSet.Debug = isDebug
	return &Renderer{
		globalContext: make(pongo2.Context),
		tplSet:        tplSet,
		logger:        logger,
	}
}

// SetGlobalContext 线程安全地设置全局上下文
func (p *Renderer) SetGlobalContext(key string, value interface{}) {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.globalContext[key] = value
}

// Warmup 预加载模板
func (p *Renderer) Warmup(templateNames ...string) error {
	for _, name := range templateNames {
		if _, err := p.tplSet.FromCache(name); err != nil {
			return fmt.Errorf("failed to warmup template '%s': %w", name, err)
		}
	}
	p.logger.WithField("count", len(templateNames)).Info("Templates warmed up")
	return nil
}

func (p *Renderer) Instance(name string, data interface{}) render.Render {
	// 安全读取全局上下文
	p.mu.RLock()
	glob := make(pongo2.Context, len(p.globalContext))
	for k, v := range p.globalContext {
		glob[k] = v
	}
	p.mu.RUnlock()

	// 解析请求数据
	var context pongo2.Context
	if data != nil {
		switch v := data.(type) {
		case gin.H:
			context = pongo2.Context(v)
		case pongo2.Context:
			context = v
		case map[string]interface{}:
			context = v
		default:
			context = make(pongo2.Context)
		}
	} else {
		context = make(pongo2.Context)
	}

	// 合并上下文（请求数据优先）
	for k, v := range glob {
		if _, exists := context[k]; !exists {
			context[k] = v
		}
	}

	// 加载模板
	tpl, err := p.tplSet.FromCache(name)
	if err != nil {
		p.logger.WithError(err).WithField("template", name).Error("Failed to load template")
		return &ErrorHTML{
			StatusCode: http.StatusInternalServerError,
			Error:      fmt.Errorf("template load error: %s", name),
		}
	}

	return &HTML{
		Template: tpl,
		Name:     name,
		Data:     context,
	}
}

type HTML struct {
	Template *pongo2.Template
	Name     string
	Data     pongo2.Context
}

func (h *HTML) Render(w http.ResponseWriter) error {
	h.WriteContentType(w)
	bytes, err := h.Template.ExecuteBytes(h.Data)
	if err != nil {
		return err
	}
	_, err = w.Write(bytes)
	return err
}

func (h *HTML) WriteContentType(w http.ResponseWriter) {
	if w.Header().Get("Content-Type") == "" {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
	}
}

// ErrorHTML 错误渲染器
type ErrorHTML struct {
	StatusCode int
	Error      error
}

func (e *ErrorHTML) Render(w http.ResponseWriter) error {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(e.StatusCode)
	_, err := w.Write([]byte(e.Error.Error()))
	return err
}

func (e *ErrorHTML) WriteContentType(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
}

// C 获取或创建 pongo2 上下文
func C(ctx *gin.Context) pongo2.Context {
	if p, exists := ctx.Get("pongo2"); exists {
		if pCtx, ok := p.(pongo2.Context); ok {
			return pCtx
		}
	}
	pCtx := make(pongo2.Context)
	ctx.Set("pongo2", pCtx)
	return pCtx
}
