// Package main is the entry point for the Charon backend API.
package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"

	"github.com/Wikid82/charon/backend/internal/api/handlers"
	"github.com/Wikid82/charon/backend/internal/api/middleware"
	"github.com/Wikid82/charon/backend/internal/api/routes"
	"github.com/Wikid82/charon/backend/internal/config"
	"github.com/Wikid82/charon/backend/internal/database"
	"github.com/Wikid82/charon/backend/internal/logger"
	"github.com/Wikid82/charon/backend/internal/models"
	"github.com/Wikid82/charon/backend/internal/server"
	"github.com/Wikid82/charon/backend/internal/version"
	"github.com/gin-gonic/gin"
	"gopkg.in/natefinch/lumberjack.v2"
)

func main() {
	// Setup logging with rotation
	logDir := "/app/data/logs"
	if err := os.MkdirAll(logDir, 0o755); err != nil {
		// Fallback to local directory if /app/data fails (e.g. local dev)
		logDir = "data/logs"
		_ = os.MkdirAll(logDir, 0o755)
	}

	logFile := filepath.Join(logDir, "charon.log")
	rotator := &lumberjack.Logger{
		Filename:   logFile,
		MaxSize:    10, // megabytes
		MaxBackups: 3,
		MaxAge:     28, // days
		Compress:   true,
	}

	// Ensure legacy cpmp.log exists as symlink for compatibility (cpmp is a legacy name for Charon)
	legacyLog := filepath.Join(logDir, "cpmp.log")
	if _, err := os.Lstat(legacyLog); os.IsNotExist(err) {
		_ = os.Symlink(logFile, legacyLog) // ignore errors
	}

	// Log to both stdout and file
	mw := io.MultiWriter(os.Stdout, rotator)
	log.SetOutput(mw)
	gin.DefaultWriter = mw
	// Initialize a basic logger so CLI and early code can log.
	logger.Init(false, mw)

	// Handle CLI commands
	if len(os.Args) > 1 && os.Args[1] == "reset-password" {
		if len(os.Args) != 4 {
			log.Fatalf("Usage: %s reset-password <email> <new-password>", os.Args[0])
		}
		email := os.Args[2]
		newPassword := os.Args[3]

		cfg, err := config.Load()
		if err != nil {
			log.Fatalf("load config: %v", err)
		}

		db, err := database.Connect(cfg.DatabasePath)
		if err != nil {
			log.Fatalf("connect database: %v", err)
		}

		var user models.User
		if err := db.Where("email = ?", email).First(&user).Error; err != nil {
			log.Fatalf("user not found: %v", err)
		}

		if err := user.SetPassword(newPassword); err != nil {
			log.Fatalf("failed to hash password: %v", err)
		}

		// Unlock account if locked
		user.LockedUntil = nil
		user.FailedLoginAttempts = 0

		if err := db.Save(&user).Error; err != nil {
			log.Fatalf("failed to save user: %v", err)
		}

		logger.Log().Infof("Password updated successfully for user %s", email)
		return
	}

	logger.Log().Infof("starting %s backend on version %s", version.Name, version.Full())

	cfg, err := config.Load()
	if err != nil {
		log.Fatalf("load config: %v", err)
	}

	db, err := database.Connect(cfg.DatabasePath)
	if err != nil {
		log.Fatalf("connect database: %v", err)
	}

	router := server.NewRouter(cfg.FrontendDir)
	// Initialize structured logger with same writer as stdlib log so both capture logs
	logger.Init(cfg.Debug, mw)
	// Request ID middleware must run before recovery so the recover logs include the request id
	router.Use(middleware.RequestID())
	// Log requests with request-scoped logger
	router.Use(middleware.RequestLogger())
	// Attach a recovery middleware that logs stack traces when debug is enabled
	router.Use(middleware.Recovery(cfg.Debug))

	// Pass config to routes for auth service and certificate service
	if err := routes.Register(router, db, cfg); err != nil {
		log.Fatalf("register routes: %v", err)
	}

	// Register import handler with config dependencies
	routes.RegisterImportHandler(router, db, cfg.CaddyBinary, cfg.ImportDir, cfg.ImportCaddyfile)

	// Check for mounted Caddyfile on startup
	if err := handlers.CheckMountedImport(db, cfg.ImportCaddyfile, cfg.CaddyBinary, cfg.ImportDir); err != nil {
		logger.Log().WithError(err).Warn("WARNING: failed to process mounted Caddyfile")
	}

	addr := fmt.Sprintf(":%s", cfg.HTTPPort)
	logger.Log().Infof("starting %s backend on %s", version.Name, addr)

	if err := router.Run(addr); err != nil {
		log.Fatalf("server error: %v", err)
	}
}
