package main

import (
	"context"
	"crypto/rand"
	"errors"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"

	"go.sour.is/pkg/env"
	"go.sour.is/pkg/lg"
	"go.sour.is/pkg/service"

	"go.sour.is/paste/v2/assets"
	"go.sour.is/paste/v2/paste"
)

var _ = apps.Register(50, func(ctx context.Context, svc *service.Harness) error {
	_, span := lg.Span(ctx)
	defer span.End()

	store := env.Default("PASTE_STORE", "data/")
	randBytes, err := strconv.ParseInt(env.Default("PASTE_RANDOM", "4096"), 10, 32)
	if err != nil {
		return err
	}

	p, err := paste.New(store)
	if err != nil {
		return err
	}

	svc.Add(&pasteSRV{
		ui:        http.FileServer(http.FS(assets.GetUI())),
		paste:     p,
		randBytes: int(randBytes),
	})

	return nil
})

type pasteSRV struct {
	ui        http.Handler
	paste     *paste.Paste
	randBytes int
}

func (a *pasteSRV) RegisterHTTP(mux *http.ServeMux) {
	mux.Handle("/ui/", lg.Htrace(http.StripPrefix("/ui/", a.ui), "paste-assets"))

	mux.Handle("/api", http.StripPrefix("/api", a))
	mux.Handle("/api/", http.StripPrefix("/api/", a))

	mux.Handle("/paste", http.StripPrefix("/paste", a))
	mux.Handle("/paste/", http.StripPrefix("/paste/", a))
}

func (p *pasteSRV) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		switch {
		case strings.HasPrefix(r.URL.Path, "rng"):
			p.getRandom(w)

		case strings.HasPrefix(r.URL.Path, "get"), r.URL.Path != "":
			p.getPaste(w, strings.TrimPrefix(r.URL.Path, "get/"))

		default:
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}

	case http.MethodPost:
		switch {
		case r.URL.Path == "":
			p.postPaste(w, r)

		default:
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}

	default:
		http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
	}
}

func (p *pasteSRV) getRandom(w http.ResponseWriter) {
	log.Println("get random")
	s := make([]byte, p.randBytes)
	_, _ = rand.Read(s)

	w.Header().Set("content-type", "application/octet-stream")
	w.WriteHeader(http.StatusOK)
	_, _ = w.Write(s)
}

func (p *pasteSRV) getPaste(w http.ResponseWriter, id string) {
	log.Println("get paste", id)
	w.Header().Set("content-type", "application/octet-stream")

	err := p.paste.Get(w, id)
	if err != nil {
		switch {
		case errors.Is(err, paste.ErrGone):
			w.WriteHeader(http.StatusGone)
		case errors.Is(err, paste.ErrNotFound):
			w.WriteHeader(http.StatusNotFound)
		case errors.Is(err, paste.ErrReadingContent):
			w.WriteHeader(http.StatusInternalServerError)
		}

		fmt.Fprintf(w, "ERR %s", err)
		return
	}
}

func (p *pasteSRV) postPaste(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("content-type", "application/octet-stream")

	id, err := p.paste.Set(r.Body)
	if err != nil {
		fmt.Fprintf(w, "ERR %s", err)
		return

	}

	log.Println("post paste", id)
	fmt.Fprint(w, "OK ", id)
}