package handlers

import (
	"strconv"
	"ttime/internal/types"

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

// 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)
	owner := claims["name"].(string)

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

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

func (gs *GState) DeleteProject(c *fiber.Ctx) error {

	projectID := c.Params("projectID")
	username := c.Params("username")

	if err := gs.Db.DeleteProject(projectID, username); err != nil {
		return c.Status(500).SendString((err.Error()))
	}

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

// GetUserProjects returns all projects that the user is a member of
func (gs *GState) GetUserProjects(c *fiber.Ctx) error {
	// First we get the username from the token
	user := c.Locals("user").(*jwt.Token)
	claims := user.Claims.(jwt.MapClaims)
	username := claims["name"].(string)

	// Then dip into the database to get the projects
	projects, err := gs.Db.GetProjectsForUser(username)
	if err != nil {
		return c.Status(500).SendString(err.Error())
	}

	// Return a json serialized list of projects
	return c.JSON(projects)
}

// ProjectRoleChange is a handler that changes a user's role within a project
func (gs *GState) ProjectRoleChange(c *fiber.Ctx) error {

	//check token and get username of current user
	user := c.Locals("user").(*jwt.Token)
	claims := user.Claims.(jwt.MapClaims)
	username := claims["name"].(string)

	// Extract the necessary parameters from the request
	data := new(types.RoleChange)
	if err := c.BodyParser(data); err != nil {
		log.Info("error parsing username, project or role")
		return c.Status(400).SendString(err.Error())
	}

	log.Info("Changing role for user: ", username, " in project: ", data.Projectname, " to: ", data.Role)

	// Dubble diping and checcking if current user is
	if ismanager, err := gs.Db.IsProjectManager(username, data.Projectname); err != nil {
		log.Warn("Error checking if projectmanager:", err)
		return c.Status(500).SendString(err.Error())
	} else if !ismanager {
		log.Warn("User is not projectmanager")
		return c.Status(401).SendString("User is not projectmanager")
	}

	// Change the user's role within the project in the database
	if err := gs.Db.ChangeUserRole(username, data.Projectname, data.Role); err != nil {
		return c.Status(500).SendString(err.Error())
	}

	// Return a success message
	return c.SendStatus(fiber.StatusOK)
}

// GetProject retrieves a specific project by its ID
func (gs *GState) GetProject(c *fiber.Ctx) error {
	// Extract the project ID from the request parameters or body
	projectID := c.Params("projectID")
	if projectID == "" {
		log.Info("No project ID provided")
		return c.Status(400).SendString("No project ID provided")
	}
	log.Info("Getting project with ID: ", projectID)

	// Parse the project ID into an integer
	projectIDInt, err := strconv.Atoi(projectID)
	if err != nil {
		log.Info("Invalid project ID")
		return c.Status(400).SendString("Invalid project ID")
	}

	// Get the project from the database by its ID
	project, err := gs.Db.GetProject(projectIDInt)
	if err != nil {
		log.Info("Error getting project:", err)
		return c.Status(500).SendString(err.Error())
	}

	// Return the project as JSON
	log.Info("Returning project: ", project.Name)
	return c.JSON(project)
}

func (gs *GState) ListAllUsersProject(c *fiber.Ctx) error {
	// Extract the project name from the request parameters or body
	projectName := c.Params("projectName")
	if projectName == "" {
		log.Info("No project name provided")
		return c.Status(400).SendString("No project name provided")
	}

	// Get the user token
	userToken := c.Locals("user").(*jwt.Token)
	claims := userToken.Claims.(jwt.MapClaims)
	username := claims["name"].(string)

	// Check if the user is a project manager for the specified project
	isManager, err := gs.Db.IsProjectManager(username, projectName)
	if err != nil {
		log.Info("Error checking project manager status:", err)
		return c.Status(500).SendString(err.Error())
	}

	// If the user is not a project manager, check if the user is a site admin
	if !isManager {
		isAdmin, err := gs.Db.IsSiteAdmin(username)
		if err != nil {
			log.Info("Error checking admin status:", err)
			return c.Status(500).SendString(err.Error())
		}
		if !isAdmin {
			log.Info("User is neither a project manager nor a site admin:", username)
			return c.Status(403).SendString("User is neither a project manager nor a site admin")
		}
	}

	// Get all users associated with the project from the database
	users, err := gs.Db.GetAllUsersProject(projectName)
	if err != nil {
		log.Info("Error getting users for project:", err)
		return c.Status(500).SendString(err.Error())
	}

	log.Info("Returning users for project: ", projectName)

	// Return the list of users as JSON
	return c.JSON(users)
}

// AddUserToProjectHandler is a handler that adds a user to a project with a specified role
func (gs *GState) AddUserToProjectHandler(c *fiber.Ctx) error {
	// Extract necessary parameters from the request
	var requestData struct {
		Username    string `json:"username"`
		ProjectName string `json:"projectName"`
		Role        string `json:"role"`
	}
	if err := c.BodyParser(&requestData); err != nil {
		log.Info("Error parsing request body:", err)
		return c.Status(400).SendString("Bad request")
	}

	// Check if the user adding another user to the project is a site admin
	user := c.Locals("user").(*jwt.Token)
	claims := user.Claims.(jwt.MapClaims)
	adminUsername := claims["name"].(string)
	log.Info("Admin username from claims:", adminUsername)

	isAdmin, err := gs.Db.IsSiteAdmin(adminUsername)
	if err != nil {
		log.Info("Error checking admin status:", err)
		return c.Status(500).SendString(err.Error())
	}

	if !isAdmin {
		log.Info("User is not a site admin:", adminUsername)
		return c.Status(403).SendString("User is not a site admin")
	}

	// Add the user to the project with the specified role
	err = gs.Db.AddUserToProject(requestData.Username, requestData.ProjectName, requestData.Role)
	if err != nil {
		log.Info("Error adding user to project:", err)
		return c.Status(500).SendString(err.Error())
	}

	// Return success message
	log.Info("User added to project successfully:", requestData.Username)
	return c.SendStatus(fiber.StatusOK)
}

// IsProjectManagerHandler is a handler that checks if a user is a project manager for a given project
func (gs *GState) IsProjectManagerHandler(c *fiber.Ctx) error {
	// Get the username from the token
	user := c.Locals("user").(*jwt.Token)
	claims := user.Claims.(jwt.MapClaims)
	username := claims["name"].(string)

	// Extract necessary parameters from the request query string
	projectName := c.Params("projectName")

	log.Info("Checking if user ", username, " is a project manager for project ", projectName)

	// Check if the user is a project manager for the specified project
	isManager, err := gs.Db.IsProjectManager(username, projectName)
	if err != nil {
		log.Info("Error checking project manager status:", err)
		return c.Status(500).SendString(err.Error())
	}

	// Return the result as JSON
	return c.JSON(fiber.Map{"isProjectManager": isManager})
}

func (gs *GState) GetProjectTimesHandler(c *fiber.Ctx) error {
    // Get the username from the token
    user := c.Locals("user").(*jwt.Token)
    claims := user.Claims.(jwt.MapClaims)
    username := claims["name"].(string)
    
    // Get project
    projectName := c.Params("projectName")
    if projectName == "" {
        log.Info("No project name provided")
        return c.Status(400).SendString("No project name provided")
    }
    
    // Get all users in the project and roles
    userProjects, err := gs.Db.GetAllUsersProject(projectName)
    if err != nil {
        log.Info("Error getting users in project:", err)
        return c.Status(500).SendString(err.Error())
    }
    
    // If the user is member
    isMember := false
    for _, userProject := range userProjects {
        if userProject.Username == username {
            isMember = true
            break
        }
    }
    
    // If the user is admin
    if !isMember {
        isAdmin, err := gs.Db.IsSiteAdmin(username)
        if err != nil {
            log.Info("Error checking admin status:", err)
            return c.Status(500).SendString(err.Error())
        }
        if !isAdmin {
            log.Info("User is neither a project member nor a site admin:", username)
            return c.Status(403).SendString("User is neither a project member nor a site admin")
        }
    }
    
    // Get project times 
    projectTimes, err := gs.Db.GetProjectTimes(projectName)
    if err != nil {
        log.Info("Error getting project times:", err)
        return c.Status(500).SendString(err.Error())
    }
    
    // Return project times as JSON
    log.Info("Returning project times for project:", projectName)
    return c.JSON(projectTimes)
}