package api

import (
	"encoding/json"
	"io"
	"net/http"

	"github.com/annybs/ezdb"
	"github.com/annybs/go/rest"
	"github.com/annybs/go/validate"
	"github.com/annybs/shorty/internal/types"
	"github.com/rs/zerolog"
)

type API struct {
	DB  ezdb.Collection[*types.Redirect]
	Log zerolog.Logger

	AllPath string
	Token   string
}

func (api *API) Delete(w http.ResponseWriter, req *http.Request) {
	if !rest.IsAuthenticated(req, api.Token) {
		w.WriteHeader(http.StatusUnauthorized)
		w.Write([]byte{})
		return
	}

	if req.URL.Path == api.AllPath {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	path := req.URL.Path

	if exist, _ := api.DB.Has(path); !exist {
		w.Write([]byte{})
		return
	}

	if err := api.DB.Delete(path); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
	} else {
		w.Write([]byte{})
	}
}

func (api *API) Get(w http.ResponseWriter, req *http.Request) {
	if req.URL.Path == api.AllPath {
		api.GetAll(w, req)
		return
	}

	path := req.URL.Path

	redirect, err := api.DB.Get(path)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(err.Error()))
		return
	}

	if len(redirect.Destination) == 0 || redirect.StatusCode == 0 {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("invalid redirect"))
	}

	w.Header().Set("Location", string(redirect.Destination))
	w.WriteHeader(redirect.StatusCode)
	w.Write([]byte{})
}

func (api *API) GetAll(w http.ResponseWriter, req *http.Request) {
	if !rest.IsAuthenticated(req, api.Token) {
		w.WriteHeader(http.StatusUnauthorized)
		w.Write([]byte{})
		return
	}

	all, err := api.DB.Iter().GetAll()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}

	data, err := json.Marshal(all)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}

	w.Write(data)
}

func (api *API) Put(w http.ResponseWriter, req *http.Request) {
	if !rest.IsAuthenticated(req, api.Token) {
		w.WriteHeader(http.StatusUnauthorized)
		w.Write([]byte{})
		return
	}

	if req.URL.Path == api.AllPath {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	path := req.URL.Path

	dest, err := io.ReadAll(req.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(err.Error()))
		return
	}
	if err := validate.URL(string(dest)); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(err.Error()))
		return
	}

	redirect := &types.Redirect{
		Destination: string(dest),
		StatusCode:  http.StatusPermanentRedirect,
	}

	if err := api.DB.Put(path, redirect); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
	} else {
		w.Write([]byte{})
	}
}

func (api *API) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	api.Log.Info().Msgf("%s %s", req.Method, req.URL.Path)

	switch req.Method {
	case http.MethodGet:
		api.Get(w, req)
	case http.MethodPut:
		api.Put(w, req)
	case http.MethodDelete:
		api.Delete(w, req)
	default:
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("unsupported method"))
	}
}