// Copyright (C) 2019 Marius Schellenberger

package server

import (
	"bytes"
	"context"
	"errors"
	"git.giftfish.de/ston1th/docstore/pkg/core"
	"git.giftfish.de/ston1th/docstore/pkg/db"
	"git.giftfish.de/ston1th/docstore/pkg/fs"
	"git.giftfish.de/ston1th/docstore/pkg/log"
	"git.giftfish.de/ston1th/docstore/pkg/scan"
	"git.giftfish.de/ston1th/godrop/v2"
	"git.giftfish.de/ston1th/jwt/v3"
	"github.com/gorilla/mux"
	"html/template"
	"net"
	"net/http"
	"time"
)

const (
	cookieName = "docstore_session"
)

type HTTPServer struct {
	Config core.Config

	listener net.Listener
	srv      *http.Server
	handler  http.Handler

	DB      *db.DB
	JWT     *jwt.JWT
	Scanner *scan.Scanner
	FS      *fs.Filesystem

	templ map[string]*template.Template
	res   map[string][]byte
}

func NewHTTPServer(cfg core.Config, l net.Listener) (srv *HTTPServer) {
	srv = &HTTPServer{
		Config:   cfg,
		listener: l,
	}
	srv.handler = srv.buildRoutes()
	srv.loadTemplates()
	srv.srv = &http.Server{}
	return
}

func (s *HTTPServer) Start() (err error) {
	s.FS, err = fs.NewFilesystem(s.Config.DataDir)
	if err != nil {
		return errors.New("fs: " + err.Error())
	}
	s.Scanner, err = scan.New(s.Config.DataDir, nil, time.Second*30)
	if err != nil {
		return errors.New("tesseract: " + err.Error())
	}
	s.DB, err = db.New(s.Config)
	if err != nil {
		return errors.New("db: " + err.Error())
	}
	err = godrop.UnveilBlock()
	if err != nil {
		return
	}
	if s.Config.Secret != "" {
		s.JWT, err = jwt.New(jwt.DefaultExpiry, s.DB, bytes.NewBufferString(s.Config.Secret))
	} else {
		s.JWT, err = jwt.New(jwt.DefaultExpiry, s.DB, nil)
	}
	if err != nil {
		return errors.New("server: " + err.Error())
	}
	if !s.DB.UserExists() {
		log.Debug("server: starting register handler")
		s.srv.Handler = s.register()
	} else {
		s.srv.Handler = s.handler
	}
	go func() {
		log.Println(s.srv.Serve(s.listener))
	}()
	return
}

func (s *HTTPServer) Stop() error {
	s.DB.Close()
	s.JWT.Stop()
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
	defer cancel()
	return s.srv.Shutdown(ctx)
}

func (s *HTTPServer) register() http.Handler {
	r := mux.NewRouter()
	r.NotFoundHandler = &registerNotFoundHandler{s}
	for _, v := range registerRoutes {
		r.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...)
	}
	return r
}

func (s *HTTPServer) buildRoutes() http.Handler {
	r := mux.NewRouter()
	r.NotFoundHandler = &notFoundHandler{s}
	for _, v := range routes {
		r.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...)
	}
	for _, v := range prefixRoutes {
		r.PathPrefix(v.Path).HandlerFunc(s.contextWrapper(v.Handler)).Methods(v.Methods...)
	}
	return r
}

func (s *HTTPServer) contextWrapper(h ctxHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		h(newContext(w, r, s))
	}
}
