package server

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

	"gitea.benny.dog/torjus/ezshare/store"
	"github.com/go-chi/chi/v5"
	"go.uber.org/zap"
)

type HTTPServer struct {
	Logger         *zap.SugaredLogger
	store          store.FileStore
	serverGRPCCert []byte
	grpcEndpoint   string

	http.Server
}

type MetadataResponse struct {
	GRPCEndpoint string `json:"grpc_endpoint"`
}

func NewHTTPSever(store store.FileStore, certBytes []byte, grpcEndpoint string) *HTTPServer {
	srv := &HTTPServer{
		Logger:         zap.NewNop().Sugar(),
		store:          store,
		serverGRPCCert: certBytes,
		grpcEndpoint:   grpcEndpoint,
	}

	r := chi.NewRouter()
	r.Get("/server.pem", srv.ServerCertHandler)
	r.Get("/metadata", srv.MetadataHandler)
	r.Get("/files/{id}", srv.FileHandler)

	srv.Handler = r
	return srv
}

func (s *HTTPServer) ServerCertHandler(w http.ResponseWriter, r *http.Request) {
	if _, err := w.Write(s.serverGRPCCert); err != nil {
		s.Logger.Warnw("Error sending server certificate.", "error", err, "remote_addr", r.RemoteAddr)

	}
	s.Logger.Infow("Sent server certificate.", "remote_addr", r.RemoteAddr)
}
func (s *HTTPServer) MetadataHandler(w http.ResponseWriter, r *http.Request) {
	md := &MetadataResponse{
		GRPCEndpoint: s.grpcEndpoint,
	}
	encoder := json.NewEncoder(w)
	if err := encoder.Encode(md); err != nil {
		s.Logger.Warnw("Error encoding or sending metadata.", "error", err, "remote_addr", r.RemoteAddr)
	}
	s.Logger.Infow("Wrote server cert.", "remote_addr", r.RemoteAddr)
}

func (s *HTTPServer) FileHandler(w http.ResponseWriter, r *http.Request) {
	id := chi.URLParam(r, "id")
	f, err := s.store.GetFile(id)
	if err != nil {
		if err == store.ErrNoSuchItem {
			s.Logger.Debugw("Tried to get non-existing file.", "remote_addr", r.RemoteAddr)
			WriteErrorResponse(w, http.StatusNotFound, "file not found")
			return
		}
		s.Logger.Warnw("Error getting file from store.", "error", err, "remote_addr", r.RemoteAddr)
		WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
		return
	}

	w.Header().Add("Content-Type", http.DetectContentType(f.Data))
	if _, err := w.Write(f.Data); err != nil {
		s.Logger.Warnw("Error sending file.", "error", err, "remote_addr", r.RemoteAddr)
	}
	s.Logger.Infow("Sent file.", "remote_addr", r.RemoteAddr)
}

func WriteErrorResponse(w http.ResponseWriter, status int, message string) {
	errMessage := struct {
		Error string `json:"error"`
	}{
		Error: message,
	}

	w.Header().Add("Content-Type", "application/json")
	w.WriteHeader(status)
	encoder := json.NewEncoder(w)
	encoder.Encode(&errMessage)
}