package main

import (
	"backend/models"
	"backend/server"
	"backend/user"
	"backend/utils"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gitea.tecamino.com/paadi/tecamino-dbm/cert"

	dbApi "gitea.tecamino.com/paadi/memberDB/api"
	"gitea.tecamino.com/paadi/tecamino-logger/logging"
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
)

func main() {
	var allowOrigins models.StringSlice

	flag.Var(&allowOrigins, "allowOrigin", "Allowed origin (can repeat this flag)")

	spa := flag.String("spa", "./dist/spa", "quasar spa files")
	workingDir := flag.String("workingDirectory", ".", "quasar spa files")
	ip := flag.String("ip", "0.0.0.0", "server listening ip")
	organization := flag.String("organization", "", "self signed ciertificate organization")
	port := flag.Uint("port", 9500, "server listening port")
	https := flag.Bool("https", false, "serves as https needs flag -cert and -chain")
	sslCert := flag.String("cert", "", "ssl certificate path")
	sslChain := flag.String("chain", "", "ssl chain path")
	debug := flag.Bool("debug", false, "log debug")
	flag.Parse()

	//change working directory only if value is given
	if *workingDir != "." && *workingDir != "" {
		os.Chdir(*workingDir)
	}

	wd, err := os.Getwd()
	if err != nil {
		log.Fatalf("Could not get working directory: %v", err)
	}

	folderName := filepath.Base(wd)
	logFileName := folderName + ".log"

	logger, err := logging.NewLogger(logFileName, &logging.Config{
		MaxSize:     1,
		MaxBackup:   3,
		MaxAge:      28,
		Debug:       *debug,
		TerminalOut: true,
	})
	if err != nil {
		logger.Error("main new logger", err.Error())
		panic(err)
	}

	//new login manager
	userManager, err := user.NewUserManager(".")
	if err != nil {
		logger.Error("main login manager", err.Error())
		panic(err)
	}

	// new server
	s := server.NewServer()

	// initiate Database handler
	dbHandler := dbApi.NewAPIHandler()

	//get local ip
	httpString := "http://"
	if *https {
		httpString = "https://"

	}
	allowOrigins = append(allowOrigins, httpString+"localhost:9000", httpString+"localhost:9500", httpString+"127.0.0.1:9500")

	localIP, err := utils.GetLocalIP()
	if err != nil {
		logger.Error("main", fmt.Sprintf("get local ip : %s", err.Error()))
	} else {
		allowOrigins = append(allowOrigins, fmt.Sprintf("%s%s:9000", httpString, localIP), fmt.Sprintf("%s%s:9500", httpString, localIP))
	}

	s.Routes.Use(cors.New(cors.Config{
		AllowOrigins:     allowOrigins,
		AllowMethods:     []string{"POST", "GET", "DELETE", "OPTIONS"},
		AllowHeaders:     []string{"Origin", "Content-Type"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: true,
		MaxAge:           12 * time.Hour,
	}))

	api := s.Routes.Group("/api")
	//set routes

	//public
	api.GET("/logout", userManager.Logout)
	api.GET("/login/me", userManager.Me)

	api.POST("/login", userManager.Login)

	//private
	auth := api.Group("/secure", user.AuthMiddleware())

	auth.GET("/users", userManager.GetUserById)
	auth.GET("/members", dbHandler.GetMemberById)
	auth.GET("/roles", userManager.GetRoleById)

	auth.POST("database/open", dbHandler.OpenDatabase)
	auth.POST("/members/add", dbHandler.AddNewMember)
	auth.POST("/members/edit", dbHandler.EditMember)
	auth.POST("/members/delete", dbHandler.DeleteMember)
	auth.POST("/members/import/csv", dbHandler.ImportCSV)

	auth.POST("/settings/update", userManager.UpdateSettings)

	auth.POST("/roles/add", userManager.AddRole)
	auth.POST("/roles/update", userManager.UpdateRole)
	auth.POST("/roles/delete", userManager.DeleteRole)

	auth.POST("/users/add", userManager.AddUser)
	auth.POST("/users/delete", userManager.DeleteUser)

	auth.POST("/login/refresh", userManager.Refresh)

	// Serve static files
	s.Routes.StaticFS("/assets", gin.Dir(filepath.Join(*spa, "assets"), true))
	s.Routes.NoRoute(func(c *gin.Context) {
		// Disallow fallback for /api paths
		if strings.HasPrefix(c.Request.URL.Path, "/api") {
			c.JSON(http.StatusNotFound, models.NewJsonErrorMessageResponse("API endpoint not found"))
			return
		}
		// Try to serve file from SPA directory
		filePath := filepath.Join(*spa, c.Request.URL.Path)
		if _, err := os.Stat(filePath); err == nil {
			c.File(filePath)
			return
		}
		// Fallback to index.html for SPA routing
		c.File(filepath.Join(*spa, "index.html"))

	})

	go func() {
		time.Sleep(500 * time.Millisecond)
		if err := utils.OpenBrowser(fmt.Sprintf("%slocalhost:%d", httpString, *port), logger); err != nil {
			logger.Error("main", fmt.Sprintf("starting browser error : %s", err.Error()))
		}
	}()

	if *https {
		if *sslCert == "" {
			logger.Error("ssl certificate", "-cert flag not given for https server")
			log.Fatal("-cert flag not given for https server")
		}
		if *sslChain == "" {
			logger.Error("ssl key", "-chain flag not given for https server")
			log.Fatal("-chain flag not given for https server")
		}

		// start https server
		logger.Info("main", fmt.Sprintf("https listen on ip: %s port: %d", *ip, *port))
		if err := s.ServeHttps(*ip, *port, cert.Cert{Organization: *organization, CertFile: *sslCert, KeyFile: *sslChain}); err != nil {
			logger.Error("main", "error https server "+err.Error())
		}
		return
	}

	// start http server
	logger.Info("main", fmt.Sprintf("http listen on ip: %s port: %d", *ip, *port))
	if err := s.ServeHttp(*ip, *port); err != nil {
		logger.Error("main", "error http server "+err.Error())
	}
}
