/*
 * SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
 * SPDX-License-Identifier: AGPL-3.0-only
 */

package app

import (
	"context"
	"dev.tandem.ws/tandem/camper/pkg/media"
	"net/http"

	"golang.org/x/text/language"

	"dev.tandem.ws/tandem/camper/pkg/auth"
	"dev.tandem.ws/tandem/camper/pkg/database"
	httplib "dev.tandem.ws/tandem/camper/pkg/http"
	"dev.tandem.ws/tandem/camper/pkg/locale"
)

type App struct {
	db              *database.DB
	fileHandler     http.Handler
	profile         *profileHandler
	admin           *adminHandler
	public          *publicHandler
	media           *media.PublicHandler
	locales         locale.Locales
	defaultLocale   *locale.Locale
	languageMatcher language.Matcher
}

func New(db *database.DB, avatarsDir string, mediaDir string) (http.Handler, error) {
	locales, err := locale.GetAll(context.Background(), db)
	if err != nil {
		return nil, err
	}
	static := http.FileServer(http.Dir("web/static"))
	profile, err := newProfileHandler(static, avatarsDir)
	if err != nil {
		return nil, err
	}
	mediaHandler, err := media.NewPublicHandler(mediaDir)
	if err != nil {
		return nil, err
	}
	app := &App{
		db:              db,
		fileHandler:     static,
		profile:         profile,
		admin:           newAdminHandler(mediaDir),
		public:          newPublicHandler(),
		media:           mediaHandler,
		locales:         locales,
		defaultLocale:   locales[language.Catalan],
		languageMatcher: language.NewMatcher(locales.Tags()),
	}

	var handler http.Handler = app
	handler = httplib.RecoverPanic(handler)
	handler = httplib.LogRequest(handler)
	return handler, nil
}

func (h *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var head string
	head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
	switch head {
	case "static":
		h.fileHandler.ServeHTTP(w, r)
	case "favicon.ico":
		r.URL.Path = head
		h.fileHandler.ServeHTTP(w, r)
	default:
		conn, err := h.db.Acquire(r.Context())
		if err != nil {
			panic(err)
		}
		defer conn.Release()

		user, err := h.getUser(r, conn)
		if err != nil {
			panic(err)
		}

		company, err := auth.CompanyByHost(r.Context(), conn, r.Host, h.locales)
		if database.ErrorIsNotFound(err) {
			http.NotFound(w, r)
			return
		} else if err != nil {
			panic(err)
		}

		switch head {
		case "":
			http.Redirect(w, r, "/"+user.Locale.Language.String()+"/", http.StatusFound)
		case "admin":
			h.admin.Handle(user, company, conn).ServeHTTP(w, r)
		case "login":
			switch r.Method {
			case http.MethodGet:
				if user.LoggedIn {
					httplib.Redirect(w, r, "/admin", http.StatusFound)
				} else {
					serveLoginForm(w, r, user, company, "/admin")
				}
			case http.MethodPost:
				handleLogin(w, r, user, company, conn)
			default:
				httplib.MethodNotAllowed(w, r, http.MethodPost, http.MethodGet)
			}
		case "me":
			h.profile.Handler(user, company, conn).ServeHTTP(w, r)
		case "media":
			h.media.Handler(user, company, conn).ServeHTTP(w, r)
		default:
			langTag, err := language.Parse(head)
			if err != nil {
				http.NotFound(w, r)
				return
			}
			urlLocale := h.locales[langTag]
			if urlLocale == nil {
				http.NotFound(w, r)
				return
			}
			user.Locale = urlLocale
			h.public.Handler(user, company, conn).ServeHTTP(w, r)
		}
	}
}