package router

import (
	"fmt"
	"net/http"
	"strings"

	"go.jolheiser.com/invitea/database"
	"go.jolheiser.com/invitea/static"

	sdk "code.gitea.io/sdk/gitea"
	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/gorilla/sessions"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	"github.com/markbates/goth/providers/gitea"
	"github.com/rs/zerolog/log"
)

type Config struct {
	Domain        string
	SessionSecret string
	Database      *database.Queries
	GiteaClient   *sdk.Client

	GiteaURL          string
	GiteaClientKey    string
	GiteaClientSecret string
	GiteaToken        string
}

func New(cfg Config) *chi.Mux {
	cfg.GiteaURL = strings.TrimRight(cfg.GiteaURL, "/")
	cfg.Domain = strings.TrimRight(cfg.Domain, "/")
	callbackURL := fmt.Sprintf("%s/auth/callback", cfg.Domain)

	goth.UseProviders(
		gitea.NewCustomisedURL(cfg.GiteaClientKey, cfg.GiteaClientSecret, callbackURL,
			fmt.Sprintf("%s/login/oauth/authorize", cfg.GiteaURL),
			fmt.Sprintf("%s/login/oauth/access_token", cfg.GiteaURL),
			fmt.Sprintf("%s/api/v1/user", cfg.GiteaURL),
		),
	)
	gothStore := sessions.NewCookieStore([]byte(cfg.GiteaClientSecret))
	gothStore.Options.HttpOnly = true
	gothic.Store = gothStore
	gothic.GetProviderName = func(_ *http.Request) (string, error) {
		return "gitea", nil
	}

	store := NewSessionStore(cfg.SessionSecret, cfg.GiteaURL)

	routes := Routes{
		DB:       cfg.Database,
		Gitea:    cfg.GiteaClient,
		GiteaURL: cfg.GiteaURL,
	}

	mux := chi.NewMux()
	mux.Use(middleware.Logger)
	mux.Use(middleware.Recoverer)

	middleware.DefaultLogger = middleware.RequestLogger(&middleware.DefaultLogFormatter{
		Logger:  &log.Logger,
		NoColor: true,
	})

	mux.Mount("/css", http.FileServer(http.FS(static.CSS)))
	mux.Mount("/img", http.FileServer(http.FS(static.Img)))

	mux.Route("/", func(r chi.Router) {
		r.Use(store.Middleware)
		r.Get("/", routes.Index)
		r.With(store.RequireAuth).Post("/", routes.IndexPost)
	})

	mux.Route("/invite/{code}", func(r chi.Router) {
		r.Get("/", routes.Invite)
		r.Post("/", routes.InvitePost)
	})

	mux.Route("/auth", func(r chi.Router) {
		r.Get("/login", func(w http.ResponseWriter, r *http.Request) {
			user, err := gothic.CompleteUserAuth(w, r)
			if err != nil {
				gothic.BeginAuthHandler(w, r)
				return
			}

			if err := store.Auth(w, r, user.AccessToken); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			http.Redirect(w, r, cfg.Domain, http.StatusFound)
		})
		r.Get("/logout", func(w http.ResponseWriter, r *http.Request) {
			if err := gothic.Logout(w, r); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			if err := store.Logout(w, r); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			http.Redirect(w, r, cfg.Domain, http.StatusFound)
		})
		r.Get("/callback", func(w http.ResponseWriter, r *http.Request) {
			user, err := gothic.CompleteUserAuth(w, r)
			if err != nil {
				http.Error(w, err.Error(), http.StatusUnauthorized)
				return
			}
			if err := store.Auth(w, r, user.AccessToken); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			http.Redirect(w, r, cfg.Domain, http.StatusFound)
		})
	})

	return mux
}