package main

import (
	"fmt"
	"time"

	"github.com/gin-gonic/gin"
	lm "github.com/hrfee/jfa-go/logmessages"
	"github.com/timshannon/badgerhold/v4"
)

// @Summary Get the names of all available profile.
// @Produce json
// @Success 200 {object} getProfileNamesDTO
// @Router /profiles/names [get]
// @Security Bearer
// @tags Profiles & Settings
func (app *appContext) GetProfileNames(gc *gin.Context) {
	fullProfileList := app.storage.GetProfiles()
	profiles := make([]string, len(fullProfileList))
	if len(profiles) != 0 {
		defaultProfile := app.storage.GetDefaultProfile()
		profiles[0] = defaultProfile.Name
		i := 1
		if len(fullProfileList) > 1 {
			app.storage.db.ForEach(badgerhold.Where("Name").Ne(profiles[0]), func(p *Profile) error {
				profiles[i] = p.Name
				i++
				return nil
			})
		}
	}
	resp := getProfileNamesDTO{
		Profiles: profiles,
	}
	gc.JSON(200, resp)
}

// @Summary Get all available profiles, indexed by their names.
// @Produce json
// @Success 200 {object} getProfilesDTO
// @Router /profiles [get]
// @Security Bearer
// @tags Profiles & Settings
func (app *appContext) GetProfiles(gc *gin.Context) {
	out := getProfilesDTO{
		DefaultProfile: app.storage.GetDefaultProfile().Name,
		Profiles:       map[string]profileDTO{},
	}
	referralsEnabled := app.config.Section("user_page").Key("referrals").MustBool(false)
	baseInv := Invite{}
	for _, p := range app.storage.GetProfiles() {
		pdto := profileDTO{
			Admin:            p.Admin,
			LibraryAccess:    p.LibraryAccess,
			FromUser:         p.FromUser,
			Ombi:             p.Ombi != nil,
			Jellyseerr:       p.Jellyseerr.Enabled,
			ReferralsEnabled: false,
		}
		if referralsEnabled {
			err := app.storage.db.Get(p.ReferralTemplateKey, &baseInv)
			if p.ReferralTemplateKey != "" && err == nil {
				pdto.ReferralsEnabled = true
			}
		}
		out.Profiles[p.Name] = pdto
	}
	gc.JSON(200, out)
}

// @Summary Set the default profile to use.
// @Produce json
// @Param profileChangeDTO body profileChangeDTO true "Default profile object"
// @Success 200 {object} boolResponse
// @Failure 500 {object} stringResponse
// @Router /profiles/default [post]
// @Security Bearer
// @tags Profiles & Settings
func (app *appContext) SetDefaultProfile(gc *gin.Context) {
	req := profileChangeDTO{}
	gc.BindJSON(&req)
	app.info.Printf(lm.SetDefaultProfile, req.Name)
	if _, ok := app.storage.GetProfileKey(req.Name); !ok {
		msg := fmt.Sprintf(lm.FailedGetProfile, req.Name)
		app.err.Println(msg)
		respond(500, msg, gc)
		return
	}
	app.storage.db.ForEach(&badgerhold.Query{}, func(profile *Profile) error {
		if profile.Name == req.Name {
			profile.Default = true
		} else {
			profile.Default = false
		}
		app.storage.SetProfileKey(profile.Name, *profile)
		return nil
	})
	respondBool(200, true, gc)
}

// @Summary Create a profile based on a Jellyfin user's settings.
// @Produce json
// @Param newProfileDTO body newProfileDTO true "New profile object"
// @Success 200 {object} boolResponse
// @Failure 500 {object} stringResponse
// @Router /profiles [post]
// @Security Bearer
// @tags Profiles & Settings
func (app *appContext) CreateProfile(gc *gin.Context) {
	var req newProfileDTO
	gc.BindJSON(&req)
	app.jf.CacheExpiry = time.Now()
	user, err := app.jf.UserByID(req.ID, false)
	if err != nil {
		app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err)
		respond(500, "Couldn't get user", gc)
		return
	}
	profile := Profile{
		FromUser:   user.Name,
		Policy:     user.Policy,
		Homescreen: req.Homescreen,
	}
	app.debug.Printf(lm.CreateProfileFromUser, user.Name)
	if req.Homescreen {
		profile.Configuration = user.Configuration
		profile.Displayprefs, err = app.jf.GetDisplayPreferences(req.ID)
		if err != nil {
			app.err.Printf(lm.FailedGetJellyfinDisplayPrefs, req.ID, err)
			respond(500, "Couldn't get displayprefs", gc)
			return
		}
	}
	app.storage.SetProfileKey(req.Name, profile)
	// Refresh discord bots, profile list
	if discordEnabled {
		app.discord.UpdateCommands()
	}
	respondBool(200, true, gc)
}

// @Summary Delete an existing profile
// @Produce json
// @Param profileChangeDTO body profileChangeDTO true "Delete profile object"
// @Success 200 {object} boolResponse
// @Router /profiles [delete]
// @Security Bearer
// @tags Profiles & Settings
func (app *appContext) DeleteProfile(gc *gin.Context) {
	req := profileChangeDTO{}
	gc.BindJSON(&req)
	name := req.Name
	app.storage.DeleteProfileKey(name)
	respondBool(200, true, gc)
}

// @Summary Enable referrals for a profile, sourced from the given invite by its code.
// @Produce json
// @Param profile path string true "name of profile to enable referrals for."
// @Param invite path string true "invite code to create referral template from."
// @Param useExpiry path string true "with-expiry or none."
// @Success 200 {object} boolResponse
// @Failure 400 {object} stringResponse
// @Failure 500 {object} stringResponse
// @Router /profiles/referral/{profile}/{invite}/{useExpiry} [post]
// @Security Bearer
// @tags Profiles & Settings
func (app *appContext) EnableReferralForProfile(gc *gin.Context) {
	profileName := gc.Param("profile")
	invCode := gc.Param("invite")
	useExpiry := gc.Param("useExpiry") == "with-expiry"
	inv, ok := app.storage.GetInvitesKey(invCode)
	if !ok {
		respond(400, "Invalid invite code", gc)
		app.err.Printf(lm.InvalidInviteCode, invCode)
		return
	}
	profile, ok := app.storage.GetProfileKey(profileName)
	if !ok {
		respond(400, "Invalid profile", gc)
		app.err.Printf(lm.FailedGetProfile, profileName)
		return
	}

	// Generate new code for referral template
	inv.Code = GenerateInviteCode()
	expiryDelta := inv.ValidTill.Sub(inv.Created)
	inv.Created = time.Now()
	if useExpiry {
		inv.ValidTill = inv.Created.Add(expiryDelta)
	} else {
		inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
	}
	inv.IsReferral = true
	inv.UseReferralExpiry = useExpiry
	// Since this is a template for multiple users, ReferrerJellyfinID is not set.
	// inv.ReferrerJellyfinID = ...

	app.storage.SetInvitesKey(inv.Code, inv)

	profile.ReferralTemplateKey = inv.Code

	app.storage.SetProfileKey(profile.Name, profile)

	respondBool(200, true, gc)
}

// @Summary Disable referrals for a profile, and removes the referral template. no-op if not enabled.
// @Produce json
// @Param profile path string true "name of profile to enable referrals for."
// @Success 200 {object} boolResponse
// @Router /profiles/referral/{profile} [delete]
// @Security Bearer
// @tags Profiles & Settings
func (app *appContext) DisableReferralForProfile(gc *gin.Context) {
	profileName := gc.Param("profile")
	profile, ok := app.storage.GetProfileKey(profileName)
	if !ok {
		respondBool(200, true, gc)
		return
	}

	app.storage.DeleteInvitesKey(profile.ReferralTemplateKey)

	profile.ReferralTemplateKey = ""

	app.storage.SetProfileKey(profileName, profile)

	respondBool(200, true, gc)
}
