package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	"github.com/rs/cors"
)

var (
	db     *gorm.DB // Declare db globally
	config *Config
)

func main() {
	// Load configuration
	config, err := loadAndValidateConfig()
	if err != nil {
		log.Fatalf("Failed to load config: %v", err)
	}

	// Connect to the database
	db, err = connectToDatabase(config)
	if err != nil {
		log.Fatalf("Failed to connect to database: %v", err)
	}
	defer db.Close()

	// Create routers
	baseRouter := mux.NewRouter()
	apiRouter := createAPIRouter(baseRouter)
	staticRouter := createStaticRouter(baseRouter, config.StaticFilesDir)

	// Set up routes
	setupAPIRoutes(apiRouter)
	setupStaticRoutes(staticRouter, config.StaticFilesDir)

	// Create CORS handler
	corsHandler := createCORSHandler(config.AllowedOrigins)

	// Start the server
	startServer(config.ListeningPort, corsHandler(baseRouter))
}

func loadAndValidateConfig() (*Config, error) {
	configFile := os.Getenv("BOXES_API_CONFIG")
	config, err := LoadConfig(configFile)
	if err != nil || config == nil {
		return nil, fmt.Errorf("failed to load config: %v", err)
	}

	logConfigDetails(config)
	return config, nil
}

func connectToDatabase(config *Config) (*gorm.DB, error) {
	db, err := ConnectDB(config.DatabasePath)
	if err != nil || db == nil {
		return nil, fmt.Errorf("failed to connect to database: %v", err)
	}
	return db, nil
}

func createAPIRouter(baseRouter *mux.Router) *mux.Router {
	return baseRouter.PathPrefix("/api/v1").Subrouter()
}

func createStaticRouter(baseRouter *mux.Router, staticPath string) *mux.Router {
	if err := validateStaticDirectory(staticPath); err != nil {
		log.Fatalf("Static directory error: %v", err)
	}
	return baseRouter.PathPrefix("/").Subrouter()
}

func setupAPIRoutes(router *mux.Router) {
	router.Handle("/login", http.HandlerFunc(LoginHandler)).Methods("POST", "OPTIONS")

	// Protected routes
	protected := router.NewRoute().Subrouter()
	protected.Use(AuthMiddleware)

	protected.Handle("/boxes", http.HandlerFunc(GetBoxesHandler)).Methods("GET", "OPTIONS")
	protected.Handle("/boxes", http.HandlerFunc(CreateBoxHandler)).Methods("POST", "OPTIONS")
	protected.Handle("/boxes/{id}", http.HandlerFunc(DeleteBoxHandler)).Methods("DELETE", "OPTIONS")
	protected.Handle("/boxes/{id}", http.HandlerFunc(GetBoxHandler)).Methods("GET", "OPTIONS")
	protected.Handle("/items", http.HandlerFunc(GetItemsHandler)).Methods("GET", "OPTIONS")
	protected.Handle("/items", http.HandlerFunc(CreateItemHandler)).Methods("POST", "OPTIONS")
	protected.Handle("/items/{id}", http.HandlerFunc(GetItemHandler)).Methods("GET", "OPTIONS")
	protected.Handle("/boxes/{id}/items", http.HandlerFunc(GetItemsInBoxHandler)).Methods("GET", "OPTIONS")
	protected.Handle("/items/{id}", http.HandlerFunc(UpdateItemHandler)).Methods("PUT", "OPTIONS")
	protected.Handle("/items/{id}", http.HandlerFunc(DeleteItemHandler)).Methods("DELETE", "OPTIONS")
	protected.Handle("/items/{id}/image", http.HandlerFunc(GetItemImageHandler)).Methods("GET", "OPTIONS")
	protected.Handle("/search/items", http.HandlerFunc(SearchItemsHandler)).Methods("GET", "OPTIONS")
	protected.Handle("/items/{id}/upload", http.HandlerFunc(UploadItemImageHandler)).Methods("POST", "OPTIONS")

	setupManagementRoutes(protected.PathPrefix("/admin").Subrouter())
}

func setupManagementRoutes(router *mux.Router) {
	router.Handle("/user", http.HandlerFunc(GetUsersHandler)).Methods("GET", "OPTIONS")
	router.Handle("/user", http.HandlerFunc(CreateUserHandler)).Methods("POST", "OPTIONS")
	router.Handle("/user/{id}", http.HandlerFunc(GetUserHandler)).Methods("GET", "OPTIONS")
	router.Handle("/user/{id}", http.HandlerFunc(DeleteUserHandler)).Methods("DELETE", "OPTIONS")
	router.Handle("/db", http.HandlerFunc(BackupDatabaseHandler)).Methods("GET", "OPTIONS")
	router.Handle("/db", http.HandlerFunc(RestoreDatabaseHandler)).Methods("POST", "OPTIONS")
}

func setupStaticRoutes(router *mux.Router, staticPath string) {
	customHandler := createCustomStaticHandler(staticPath)
	router.PathPrefix("/").Handler(http.StripPrefix("/", customHandler))
}

func createCustomStaticHandler(staticPath string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		log.Printf("Attempting to serve: %s from directory: %s", r.URL.Path, staticPath)
		fullPath := filepath.Join(staticPath, r.URL.Path)
		if _, err := os.Stat(fullPath); os.IsNotExist(err) {
			log.Printf("File not found: %s", fullPath)
			http.NotFound(w, r)
			return
		}
		http.FileServer(http.Dir(staticPath)).ServeHTTP(w, r)
	}
}

func createCORSHandler(allowedOrigins string) func(http.Handler) http.Handler {
	origins := strings.Split(allowedOrigins, ",")
	if len(origins) == 0 {
		origins = []string{"http://localhost:3000"}
	}

	return cors.New(cors.Options{
		AllowedOrigins:   origins,
		AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		AllowedHeaders:   []string{"Authorization", "Content-Type"},
		ExposedHeaders:   []string{"Content-Length", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Cache-Control", "Content-Language", "Content-Type", "Expires", "Last-Modified", "Pragma", "ETag"},
		AllowCredentials: true,
	}).Handler
}

func startServer(port int, handler http.Handler) {
	fmt.Printf("Server listening on port %d\n", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), handler))
}

func validateStaticDirectory(path string) error {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return fmt.Errorf("static directory does not exist: %s", path)
	}
	return nil
}

func logConfigDetails(config *Config) {
	fmt.Println("Config details:")
	fmt.Printf("Database Path: %s\n", config.DatabasePath)
	fmt.Printf("Image Storage Dir: %s\n", config.ImageStorageDir)
	fmt.Printf("JWT Secret: %s\n", config.JWTSecret)
	fmt.Printf("Log File: %s\n", config.LogFile)
	fmt.Printf("Listening Port: %d\n", config.ListeningPort)
	fmt.Printf("Allowed Origins: %s\n", config.AllowedOrigins)
}
