package main

import (
	"context"
	"os"
	"strings"

	swagger "github.com/arsmn/fiber-swagger/v2"
	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
	"github.com/jackc/pgx/v4/pgxpool"
	"github.com/joho/godotenv"
	"gitlab.larvit.se/power-plan/auth/src/db"
	h "gitlab.larvit.se/power-plan/auth/src/handlers"
	"gitlab.larvit.se/power-plan/auth/src/utils"
	"go.uber.org/zap"

	// docs are generated by Swag CLI, you have to import them.
	_ "gitlab.larvit.se/power-plan/auth/src/docs"
)

// Don't put in utils, because it creates import cycle with db... just left it here for now
func createAdminAccount(Db db.Db, log *zap.SugaredLogger) {
	adminAccountID, uuidErr := uuid.NewRandom()
	if uuidErr != nil {
		log.Fatal("Could not create new Uuid", "err", uuidErr.Error())
	}
	_, adminAccountErr := Db.AccountCreate(db.AccountCreateInput{
		ID:       adminAccountID,
		Name:     "admin",
		APIKey:   os.Getenv("ADMIN_API_KEY"),
		Password: "",
		Fields:   []db.AccountCreateInputFields{{Name: "role", Values: []string{"admin"}}},
	})
	if adminAccountErr != nil && strings.HasPrefix(adminAccountErr.Error(), "ERROR: duplicate key") {
		log.Info("Admin account already created, nothing written to database")
	} else if adminAccountErr != nil {
		log.Fatal("Could not create admin account", "err", adminAccountErr.Error())
	}
}

// @title JWT Auth API
// @version 0.1
// @description This is a tiny http API for auth. Register accounts, auth with api-key or name/password, renew JWT tokens...

// @contact.name Power Plan
// @contact.url https://pwrpln.com/
// @contact.email lilleman@larvit.se

// @license.name MIT

// @BasePath /
func main() {
	log := utils.GetLog()

	err := godotenv.Load()
	if err != nil {
		log.Warn("Error loading .env file, this could be ok if the env file does not exist", "err", err.Error())
	}

	if os.Getenv("JWT_SHARED_SECRET") == "changeMe" {
		log.Fatal("You must change JWT_SHARED_SECRET in .env")
	}
	if os.Getenv("ADMIN_API_KEY") == "changeMe" {
		log.Fatal("You must change ADMIN_API_KEY in .env")
	}
	jwtKey := []byte(os.Getenv("JWT_SHARED_SECRET"))

	dbPool, err := pgxpool.Connect(context.Background(), os.Getenv("DATABASE_URL"))
	if err != nil {
		log.Fatal("Failed to open DB connection", "err", err.Error())
	} else {
		log.Info("Connected to PostgreSQL database")
	}
	defer dbPool.Close()

	app := fiber.New()

	Db := db.Db{DbPool: dbPool, Log: log}
	handlers := h.Handlers{Db: Db, JwtKey: jwtKey, Log: log}

	createAdminAccount(Db, log)

	// Log all requests
	app.Use(handlers.LogReq)

	// Always require application/json
	app.Use(handlers.RequireJSON)

	app.Get("/", func(c *fiber.Ctx) error { return c.Redirect("/swagger/index.html") })
	app.Get("/swagger", func(c *fiber.Ctx) error { return c.Redirect("/swagger/index.html") })
	app.Get("/swagger/*", swagger.Handler)

	app.Get("/account/:accountID", handlers.AccountGet)
	app.Post("/account", handlers.AccountCreate)
	app.Post("/auth/api-key", handlers.AccountAuthAPIKey)
	app.Post("/auth/password", handlers.AccountAuthPassword)
	app.Post("/renew-token", handlers.RenewToken)

	log.Info("Trying to start web server", "WEB_BIND_HOST", os.Getenv("WEB_BIND_HOST"))

	if err := app.Listen(os.Getenv("WEB_BIND_HOST")); err != nil {
		log.Fatal("Could not start web server", "err", err.Error())
	}

	log.Info("Webb server closed, shutting down")
}
