package gmi

import (
	"bytes"
	"context"
	"io"
	"sync"

	"git.sr.ht/~adnano/go-gemini"
	"github.com/mediocregopher/blog.mediocregopher.com/srv/cache"
)

type cacheRW struct {
	gemini.ResponseWriter
	status    gemini.Status
	mediaType string
	buf       []byte
}

func (c *cacheRW) SetMediaType(mediaType string) {
	c.mediaType = mediaType
	c.ResponseWriter.SetMediaType(mediaType)
}

func (c *cacheRW) WriteHeader(status gemini.Status, meta string) {
	c.status = status
	c.ResponseWriter.WriteHeader(status, meta)
}

func (c *cacheRW) Write(b []byte) (int, error) {
	c.buf = append(c.buf, b...)
	return c.ResponseWriter.Write(b)
}

func cacheMiddleware(cache cache.Cache) func(h gemini.Handler) gemini.Handler {

	type entry struct {
		mediaType string
		body      []byte
	}

	pool := sync.Pool{
		New: func() interface{} { return new(bytes.Reader) },
	}

	return func(h gemini.Handler) gemini.Handler {
		return gemini.HandlerFunc(func(
			ctx context.Context,
			rw gemini.ResponseWriter,
			r *gemini.Request,
		) {

			id := r.URL.String()

			if value := cache.Get(id); value != nil {

				entry := value.(entry)

				if entry.mediaType != "" {
					rw.SetMediaType(entry.mediaType)
				}

				reader := pool.Get().(*bytes.Reader)
				defer pool.Put(reader)

				reader.Reset(entry.body)

				io.Copy(rw, reader)
				return
			}

			cacheRW := &cacheRW{
				ResponseWriter: rw,
				status:         gemini.StatusSuccess,
			}

			h.ServeGemini(ctx, cacheRW, r)

			if cacheRW.status == gemini.StatusSuccess {
				cache.Set(id, entry{
					mediaType: cacheRW.mediaType,
					body:      cacheRW.buf,
				})
			}
		})
	}
}
