package handlers

import (
	"time"
	"ttime/internal/database"
	"ttime/internal/types"

	"github.com/gofiber/fiber/v2"
	"github.com/golang-jwt/jwt/v5"
)

// The actual interface that we will use
type GlobalState interface {
	Register(c *fiber.Ctx) error      // To register a new user
	UserDelete(c *fiber.Ctx) error    // To delete a user
	Login(c *fiber.Ctx) error         // To get the token
	LoginRenew(c *fiber.Ctx) error    // To renew the token
	CreateProject(c *fiber.Ctx) error // To create a new project
	// GetProjects(c *fiber.Ctx) error          // To get all projects
	// GetProject(c *fiber.Ctx) error           // To get a specific project
	// UpdateProject(c *fiber.Ctx) error        // To update a project
	// DeleteProject(c *fiber.Ctx) error        // To delete a project
	// CreateTask(c *fiber.Ctx) error           // To create a new task
	// GetTasks(c *fiber.Ctx) error             // To get all tasks
	// GetTask(c *fiber.Ctx) error              // To get a specific task
	// UpdateTask(c *fiber.Ctx) error           // To update a task
	// DeleteTask(c *fiber.Ctx) error           // To delete a task
	// CreateCollection(c *fiber.Ctx) error     // To create a new collection
	// GetCollections(c *fiber.Ctx) error       // To get all collections
	// GetCollection(c *fiber.Ctx) error        // To get a specific collection
	// UpdateCollection(c *fiber.Ctx) error     // To update a collection
	// DeleteCollection(c *fiber.Ctx) error     // To delete a collection
	// SignCollection(c *fiber.Ctx) error       // To sign a collection
	GetButtonCount(c *fiber.Ctx) error       // For demonstration purposes
	IncrementButtonCount(c *fiber.Ctx) error // For demonstration purposes
}

// "Constructor"
func NewGlobalState(db database.Database) GlobalState {
	return &GState{Db: db, ButtonCount: 0}
}

// The global state, which implements all the handlers
type GState struct {
	Db          database.Database
	ButtonCount int
}

// Register is a simple handler that registers a new user
//
//	@Summary		Register a new user
//	@Description	Register a new user
//	@Tags			User
//	@Accept			json
//	@Produce		json
//	@Success		200	{string}	string	"User added"
//	@Failure		400	{string}	string	"Bad request"
//	@Failure		500	{string}	string	"Internal server error"
//	@Router			/api/register [post]
func (gs *GState) Register(c *fiber.Ctx) error {
	u := new(types.NewUser)
	if err := c.BodyParser(u); err != nil {
		return c.Status(400).SendString(err.Error())
	}

	if err := gs.Db.AddUser(u.Username, u.Password); err != nil {
		return c.Status(500).SendString(err.Error())
	}

	return c.Status(200).SendString("User added")
}

// This path should obviously be protected in the future
// UserDelete deletes a user from the database
func (gs *GState) UserDelete(c *fiber.Ctx) error {
	u := new(types.User)
	if err := c.BodyParser(u); err != nil {
		return c.Status(400).SendString(err.Error())
	}

	if err := gs.Db.RemoveUser(u.Username); err != nil {
		return c.Status(500).SendString(err.Error())
	}

	return c.Status(200).SendString("User deleted")
}

func (gs *GState) GetButtonCount(c *fiber.Ctx) error {
	return c.Status(200).JSON(fiber.Map{"pressCount": gs.ButtonCount})
}

func (gs *GState) IncrementButtonCount(c *fiber.Ctx) error {
	gs.ButtonCount++
	return c.Status(200).JSON(fiber.Map{"pressCount": gs.ButtonCount})
}

// Login is a simple login handler that returns a JWT token
func (gs *GState) Login(c *fiber.Ctx) error {
	// To test: curl --data "user=user&pass=pass" http://localhost:8080/api/login
	user := c.FormValue("user")
	pass := c.FormValue("pass")

	// Throws Unauthorized error
	if user != "user" || pass != "pass" {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

	// Create the Claims
	claims := jwt.MapClaims{
		"name":  user,
		"admin": false,
		"exp":   time.Now().Add(time.Hour * 72).Unix(),
	}

	// Create token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// Generate encoded token and send it as response.
	t, err := token.SignedString([]byte("secret"))
	if err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	return c.JSON(fiber.Map{"token": t})
}

// LoginRenew is a simple handler that renews the token
func (gs *GState) LoginRenew(c *fiber.Ctx) error {
	// For testing: curl localhost:3000/restricted -H "Authorization: Bearer <token>"
	user := c.Locals("user").(*jwt.Token)
	claims := user.Claims.(jwt.MapClaims)
	claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
	renewed := jwt.MapClaims{
		"name":  claims["name"],
		"admin": claims["admin"],
		"exp":   claims["exp"],
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, renewed)
	t, err := token.SignedString([]byte("secret"))
	if err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}
	return c.JSON(fiber.Map{"token": t})
}

// CreateProject is a simple handler that creates a new project
func (gs *GState) CreateProject(c *fiber.Ctx) error {
	user := c.Locals("user").(*jwt.Token)

	p := new(types.NewProject)
	if err := c.BodyParser(p); err != nil {
		return c.Status(400).SendString(err.Error())
	}

	// Get the username from the token and set it as the owner of the project
	// This is ugly but
	claims := user.Claims.(jwt.MapClaims)
	p.Owner = claims["name"].(string)

	if err := gs.Db.AddProject(p.Name, p.Description, p.Owner); err != nil {
		return c.Status(500).SendString(err.Error())
	}

	return c.Status(200).SendString("Project added")
}