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

package campsite

import (
	"context"
	"net/http"
	"time"

	"golang.org/x/text/language"

	"dev.tandem.ws/tandem/camper/pkg/auth"
	"dev.tandem.ws/tandem/camper/pkg/campsite/types"
	"dev.tandem.ws/tandem/camper/pkg/carousel"
	"dev.tandem.ws/tandem/camper/pkg/database"
	httplib "dev.tandem.ws/tandem/camper/pkg/http"
	"dev.tandem.ws/tandem/camper/pkg/locale"
	"dev.tandem.ws/tandem/camper/pkg/season"
	"dev.tandem.ws/tandem/camper/pkg/template"
)

type PublicHandler struct {
	types *types.PublicHandler
}

func NewPublicHandler() *PublicHandler {
	return &PublicHandler{
		types: &types.PublicHandler{},
	}
}

func (h *PublicHandler) Handler(user *auth.User, company *auth.Company, conn *database.Conn) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var head string
		head, r.URL.Path = httplib.ShiftPath(r.URL.Path)

		switch head {
		case "types":
			h.types.Handler(user, company, conn).ServeHTTP(w, r)
		case "calendar":
			h.calendarHandler(user, company, conn).ServeHTTP(w, r)
		default:
			page, err := newPublicPage(r.Context(), company, conn, user.Locale, head)
			if database.ErrorIsNotFound(err) {
				http.NotFound(w, r)
				return
			} else if err != nil {
				panic(err)
			}

			head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
			switch head {
			case "":
				switch r.Method {
				case http.MethodGet:
					page.MustRender(w, r, user, company, conn)
				default:
					httplib.MethodNotAllowed(w, r, http.MethodGet)
				}
			default:
				http.NotFound(w, r)
			}
		}
	})
}

func (h *PublicHandler) calendarHandler(user *auth.User, company *auth.Company, conn *database.Conn) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var head string
		head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
		switch head {
		case "":
			switch r.Method {
			case http.MethodGet:
				year := season.GetCalendarYear(r.URL.Query())
				month := season.GetCalendarMonth(r.URL.Query())
				date := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC).AddDate(0, 1, 0)
				calendar, err := season.CollectSeasonCalendar(r.Context(), company, conn, date.Year(), date.Month(), 3)
				if err != nil {
					panic(err)
				}
				template.MustRenderPublicNoLayout(w, r, user, company, "campsite/calendar.gohtml", calendar)
			default:
				httplib.MethodNotAllowed(w, r, http.MethodGet)
			}
		default:
			http.NotFound(w, r)
		}
	})
}

type publicPage struct {
	*template.PublicPage
	TypeName string
	Label    string
	Carousel []*carousel.Slide
	Features []*feature
	Info     []string
}

func newPublicPage(ctx context.Context, company *auth.Company, conn *database.Conn, loc *locale.Locale, label string) (*publicPage, error) {
	page := &publicPage{
		PublicPage: template.NewPublicPage(),
		Label:      label,
		Carousel:   mustCollectSlides(ctx, conn, company, loc, label),
	}
	row := conn.QueryRow(ctx, `
		select coalesce(type_i18n.name, campsite_type.name) as l10n_type_name
		     , array[coalesce(campsite_i18n.info1, campsite.info1)::text, coalesce(campsite_i18n.info2, campsite.info2)::text] as info
		from campsite
		    join campsite_type using (campsite_type_id)
		    left join campsite_type_i18n as type_i18n on campsite_type.campsite_type_id = type_i18n.campsite_type_id and type_i18n.lang_tag = $1
		    left join campsite_i18n on campsite.campsite_id = campsite_i18n.campsite_id and campsite_i18n.lang_tag = $1
		where campsite_type.company_id = $2
		  and label = $3
		  and campsite.active
	`, loc.Language, company.ID, label)
	if err := row.Scan(&page.TypeName, &page.Info); err != nil {
		return nil, err
	}
	var err error
	page.Features, err = collectFeatures(ctx, conn, company, loc.Language, label)
	if err != nil {
		return nil, err
	}

	return page, nil
}

func (p *publicPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
	p.Setup(r, user, company, conn)
	template.MustRenderPublic(w, r, user, company, "campsite/page.gohtml", p)
}

type feature struct {
	Icon string
	Name string
}

func collectFeatures(ctx context.Context, conn *database.Conn, company *auth.Company, language language.Tag, label string) ([]*feature, error) {
	rows, err := conn.Query(ctx, `
		select feature.icon_name
		     , coalesce(i18n.name, feature.name) as l10n_name
        from campsite_feature as feature
            join campsite using (campsite_id)
		    left join campsite_feature_i18n as i18n on feature.campsite_feature_id = i18n.campsite_feature_id and i18n.lang_tag = $1
		where campsite.label = $2
		  and campsite.company_id = $3
		order by feature.position
    `, language, label, company.ID)
	if err != nil {
		return nil, err
	}

	var features []*feature
	for rows.Next() {
		f := &feature{}
		if err = rows.Scan(&f.Icon, &f.Name); err != nil {
			return nil, err
		}
		features = append(features, f)
	}
	return features, nil
}
