package controllers

import (
	"errors"
	"strings"

	httpmw "trustcontact/internal/http/middleware"
	"trustcontact/internal/services"

	"github.com/gofiber/fiber/v2"
)

type AuthController struct {
	authService *services.AuthService
}

func NewAuthController(authService *services.AuthService) *AuthController {
	return &AuthController{authService: authService}
}

func (ac *AuthController) ShowHome(c *fiber.Ctx) error {
	return renderPublic(c, "home.html", map[string]any{
		"Title":      "Home",
		"NavSection": "home",
	})
}

func (ac *AuthController) ShowSignup(c *fiber.Ctx) error {
	return renderPublic(c, "signup.html", map[string]any{
		"Title":      "Sign up",
		"NavSection": "public",
	})
}

func (ac *AuthController) Signup(c *fiber.Ctx) error {
	email := strings.TrimSpace(c.FormValue("email"))
	password := c.FormValue("password")

	if err := ac.authService.Signup(c.UserContext(), email, password); err != nil {
		if errors.Is(err, services.ErrEmailAlreadyExists) {
			httpmw.SetTemplateData(c, "FlashError", "Email gia registrata")
		} else {
			httpmw.SetTemplateData(c, "FlashError", "Impossibile completare la registrazione")
		}
		return renderPublic(c, "signup.html", map[string]any{
			"Title":      "Sign up",
			"NavSection": "public",
			"Email":      email,
		})
	}

	if err := httpmw.SetFlashSuccess(c, "Registrazione completata. Controlla la tua email per verificare l'account."); err != nil {
		return err
	}
	return c.Redirect("/verify-notice")
}

func (ac *AuthController) ShowLogin(c *fiber.Ctx) error {
	return renderPublic(c, "login.html", map[string]any{
		"Title":      "Login",
		"NavSection": "public",
	})
}

func (ac *AuthController) Login(c *fiber.Ctx) error {
	email := strings.TrimSpace(c.FormValue("email"))
	password := c.FormValue("password")

	user, err := ac.authService.Login(email, password)
	if err != nil {
		switch {
		case errors.Is(err, services.ErrEmailNotVerified):
			httpmw.SetTemplateData(c, "FlashError", "Email non verificata. Controlla la posta.")
		case errors.Is(err, services.ErrInvalidCredentials):
			httpmw.SetTemplateData(c, "FlashError", "Credenziali non valide")
		default:
			httpmw.SetTemplateData(c, "FlashError", "Errore durante il login")
		}

		return renderPublic(c, "login.html", map[string]any{
			"Title":      "Login",
			"NavSection": "public",
			"Email":      email,
		})
	}

	if err := httpmw.SetSessionUserID(c, user.ID); err != nil {
		return err
	}
	if err := httpmw.SetFlashSuccess(c, "Login effettuato"); err != nil {
		return err
	}
	return c.Redirect("/private")
}

func (ac *AuthController) Logout(c *fiber.Ctx) error {
	if err := httpmw.ClearSessionUser(c); err != nil {
		return err
	}
	if err := httpmw.SetFlashSuccess(c, "Logout effettuato"); err != nil {
		return err
	}
	return c.Redirect("/login")
}

func (ac *AuthController) VerifyEmail(c *fiber.Ctx) error {
	token := strings.TrimSpace(c.Query("token"))
	if token == "" {
		httpmw.SetTemplateData(c, "FlashError", "Token non valido")
		return renderPublic(c, "verify_notice.html", map[string]any{
			"Title":      "Verifica email",
			"NavSection": "public",
		})
	}

	if err := ac.authService.VerifyEmail(token); err != nil {
		httpmw.SetTemplateData(c, "FlashError", "Token non valido o scaduto")
		return renderPublic(c, "verify_notice.html", map[string]any{
			"Title":      "Verifica email",
			"NavSection": "public",
		})
	}

	if err := httpmw.SetFlashSuccess(c, "Email verificata. Ora puoi accedere."); err != nil {
		return err
	}
	return c.Redirect("/login")
}

func (ac *AuthController) ShowVerifyNotice(c *fiber.Ctx) error {
	return renderPublic(c, "verify_notice.html", map[string]any{
		"Title":      "Verifica email",
		"NavSection": "public",
	})
}

func (ac *AuthController) ShowForbidden(c *fiber.Ctx) error {
	return renderPublic(c, "forbidden.html", map[string]any{
		"Title":      "Forbidden",
		"NavSection": "public",
	})
}

func (ac *AuthController) ShowForgotPassword(c *fiber.Ctx) error {
	return renderPublic(c, "forgot_password.html", map[string]any{
		"Title":      "Forgot password",
		"NavSection": "public",
	})
}

func (ac *AuthController) ForgotPassword(c *fiber.Ctx) error {
	email := strings.TrimSpace(c.FormValue("email"))
	if err := ac.authService.ForgotPassword(c.UserContext(), email); err != nil {
		httpmw.SetTemplateData(c, "FlashError", "Impossibile elaborare la richiesta")
		return renderPublic(c, "forgot_password.html", map[string]any{
			"Title":      "Forgot password",
			"NavSection": "public",
			"Email":      email,
		})
	}

	httpmw.SetTemplateData(c, "FlashSuccess", "Se l'account esiste, riceverai una email con le istruzioni.")
	return renderPublic(c, "forgot_password.html", map[string]any{
		"Title":      "Forgot password",
		"NavSection": "public",
	})
}

func (ac *AuthController) ShowResetPassword(c *fiber.Ctx) error {
	token := strings.TrimSpace(c.Query("token"))
	return renderPublic(c, "reset_password.html", map[string]any{
		"Title":      "Reset password",
		"NavSection": "public",
		"Token":      token,
	})
}

func (ac *AuthController) ResetPassword(c *fiber.Ctx) error {
	token := strings.TrimSpace(c.Query("token"))
	password := c.FormValue("password")

	if token == "" {
		httpmw.SetTemplateData(c, "FlashError", "Token non valido")
		return renderPublic(c, "reset_password.html", map[string]any{
			"Title":      "Reset password",
			"NavSection": "public",
		})
	}

	if err := ac.authService.ResetPassword(token, password); err != nil {
		httpmw.SetTemplateData(c, "FlashError", "Token non valido o scaduto")
		return renderPublic(c, "reset_password.html", map[string]any{
			"Title":      "Reset password",
			"NavSection": "public",
			"Token":      token,
		})
	}

	if err := httpmw.SetFlashSuccess(c, "Password aggiornata. Effettua il login."); err != nil {
		return err
	}
	return c.Redirect("/login")
}

func (ac *AuthController) UpdateLanguage(c *fiber.Ctx) error {
	currentUser, ok := httpmw.CurrentUserFromContext(c)
	if !ok {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

	type langRequest struct {
		Lang string `json:"lang" form:"lang"`
	}

	var req langRequest
	if err := c.BodyParser(&req); err != nil {
		req.Lang = c.FormValue("lang")
	}

	lang := services.NormalizeLanguage(req.Lang)
	if !services.IsSupportedLanguage(lang) {
		return c.Status(fiber.StatusBadRequest).SendString("invalid language")
	}

	if err := ac.authService.UpdateUserLanguage(currentUser.ID, lang); err != nil {
		if errors.Is(err, services.ErrInvalidLanguage) {
			return c.Status(fiber.StatusBadRequest).SendString("invalid language")
		}
		return c.Status(fiber.StatusInternalServerError).SendString("cannot update language")
	}

	return c.SendStatus(fiber.StatusNoContent)
}

func (ac *AuthController) UpdateTheme(c *fiber.Ctx) error {
	currentUser, ok := httpmw.CurrentUserFromContext(c)
	if !ok {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

	type themeRequest struct {
		Theme string `json:"theme" form:"theme"`
	}

	var req themeRequest
	if err := c.BodyParser(&req); err != nil {
		req.Theme = c.FormValue("theme")
	}

	theme := services.NormalizeTheme(req.Theme)
	if theme != "dark" && theme != "light" {
		return c.Status(fiber.StatusBadRequest).SendString("invalid theme")
	}

	if err := ac.authService.UpdateUserTheme(currentUser.ID, theme); err != nil {
		if errors.Is(err, services.ErrInvalidTheme) {
			return c.Status(fiber.StatusBadRequest).SendString("invalid theme")
		}
		return c.Status(fiber.StatusInternalServerError).SendString("cannot update theme")
	}

	return c.SendStatus(fiber.StatusNoContent)
}
