package main

import (
	"fmt"
	"os"
	_ "ttime/docs"
	"ttime/internal/config"
	"ttime/internal/database"
	"ttime/internal/handlers/projects"
	"ttime/internal/handlers/reports"
	"ttime/internal/handlers/users"

	"github.com/BurntSushi/toml"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/logger"
	"github.com/gofiber/swagger"

	jwtware "github.com/gofiber/contrib/jwt"
)

//	@title			TTime API
//	@version		0.0.1
//	@description	This is the API for TTime, a time tracking application.

//	@license.name	AGPL
//	@license.url	https://www.gnu.org/licenses/agpl-3.0.html

//	@securityDefinitions.apikey	JWT
//	@in							header
//	@name						Authorization
//	@description				Use the JWT token provided by the login endpoint to authenticate requests.  **Prefix the token with "Bearer ".**

//	@host		localhost:8080
//	@BasePath	/api

//	@externalDocs.description	OpenAPI
//	@externalDocs.url			https://swagger.io/resources/open-api/

/**
Main function for starting the server and initializing configurations.
Reads configuration from file, pretty prints it, connects to the database,
migrates it, and sets up routes for the server.
*/

func main() {
	conf, err := config.ReadConfigFromFile("config.toml")
	if err != nil {
		conf = config.NewConfig()
		_ = conf.WriteConfigToFile("config.toml")
	}

	// Pretty print the current config with toml
	_ = toml.NewEncoder(os.Stdout).Encode(conf)

	fmt.Printf("Starting server on http://localhost:%d\n", conf.Port)
	fmt.Printf("For documentation, go to http://localhost:%d/swagger/index.html\n", conf.Port)

	// Connect to the database
	db := database.DbConnect(conf.DbPath)

	// Migrate the database
	if err = db.Migrate(); err != nil {
		fmt.Println("Error migrating database: ", err)
		os.Exit(1)
	}

	// Migrate sample data, should not be used in production
	if err = db.MigrateSampleData(); err != nil {
		fmt.Println("Error migrating sample data: ", err)
		os.Exit(1)
	}

	// Create the server
	server := fiber.New()

	// We want some logs
	server.Use(logger.New())

	// Sets up db middleware, accessed as Local "db" key
	server.Use(database.DbMiddleware(&db))

	// Mounts the swagger documentation, this is available at /swagger/index.html
	server.Get("/swagger/*", swagger.HandlerDefault)

	// Mount our static files (Beware of the security implications of this!)
	// This will likely be replaced by an embedded filesystem in the future
	server.Static("/", "./static")

	// Create a group for our API
	api := server.Group("/api")

	// Register our unprotected routes
	api.Post("/register", users.Register)
	api.Post("/login", users.Login)

	// Every route from here on will require a valid
	// JWT bearer token authentication in the header
	server.Use(jwtware.New(jwtware.Config{
		SigningKey: jwtware.SigningKey{Key: []byte("secret")},
	}))

	// All user related routes
	// userGroup := api.Group("/user") // Not currently in use
	api.Get("/users/all", users.ListAllUsers)
	api.Get("/project/getAllUsers", users.GetAllUsersProject)
	api.Get("/username", users.GetUserName)
	api.Post("/login", users.Login)
	api.Post("/register", users.Register)
	api.Post("/loginrenew", users.LoginRenew)
	api.Post("/promoteToAdmin", users.PromoteToAdmin)
	api.Put("/changeUserName", users.ChangeUserName)
	api.Delete("/userdelete/:username", users.UserDelete) // Perhaps just use POST to avoid headaches

	// All project related routes
	// projectGroup := api.Group("/project") // Not currently in use
	api.Get("/getProjectTimes/:projectName", projects.GetProjectTimesHandler)
	api.Get("/getUserProjects/:username", projects.GetUserProjects)
	api.Get("/project/:projectId", projects.GetProject)
	api.Get("/checkIfProjectManager/:projectName", projects.IsProjectManagerHandler)
	api.Get("/getUsersProject/:projectName", projects.ListAllUsersProject)
	api.Post("/project", projects.CreateProject)
	api.Post("/ProjectRoleChange", projects.ProjectRoleChange)
	api.Put("/promoteToPm/:projectName", projects.PromoteToPm)
	api.Put("/addUserToProject/:projectName", projects.AddUserToProjectHandler)
	api.Delete("/removeUserFromProject/:projectName", projects.RemoveUserFromProject)
	api.Delete("/removeProject/:projectName", projects.RemoveProject)
	api.Delete("/project/:projectID", projects.DeleteProject)

	// All report related routes
	// reportGroup := api.Group("/report") // Not currently in use
	api.Get("/getWeeklyReport", reports.GetWeeklyReport)
	api.Get("/getUnsignedReports/:projectName", reports.GetUnsignedReports)
	api.Get("/getAllWeeklyReports/:projectName", reports.GetAllWeeklyReports)
	api.Post("/submitWeeklyReport", reports.SubmitWeeklyReport)
	api.Put("/signReport/:reportId", reports.SignReport)
	api.Put("/updateWeeklyReport", reports.UpdateWeeklyReport)

	// Announce the port we are listening on and start the server
	err = server.Listen(fmt.Sprintf(":%d", conf.Port))
	if err != nil {
		fmt.Println("Error starting server: ", err)
	}
}