//go:build !prod
// +build !prod

package main

import (
	"context"
	"crypto/rand"
	"embed"
	"encoding/hex"
	"fmt"
	"io/fs"
	"log"
	"log/slog"
	"mva/datastar"
	"mva/sqlite"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

const (
	exitCodeErr       = 1
	exitCodeInterrupt = 2
)

//go:embed frontend
var frontend embed.FS

// //go:embed templates
// var templates embed.FS

// var templ = template.Must(template.ParseFS(templates, "templates/*.html"))

// main is the entry point of the application.
// Its task is to check wether all execution conditions are fullfilled.
// Collecting information from the environment: flags, environment vars, configs.
// Calling the run() function.
func main() {

	fmt.Println("Developement mode")

	ctx := context.Background()

	// logging
	logFileName := "./mva.log"
	logFile, err := os.OpenFile(logFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		log.Printf("error opening file: %v", err)
		os.Exit(exitCodeErr)
	}
	defer logFile.Close()
	log.SetOutput(logFile)

	// database
	dbName := "./database/advendtureworks.db"
	db := sqlite.New(dbName)
	if err != nil {
		log.Printf("Failed to open %s database: %v", dbName, err)
		os.Exit(exitCodeErr)
	}

	// run the app
	if err := run(db, ctx); err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		os.Exit(exitCodeErr)
	}

}

// Setting up all dependencies
// Creating the server (a central http handler)
func run(db *sqlite.Database, ctx context.Context) error {

	ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
	defer cancel()

	err := db.Open(ctx)
	if err != nil {
		log.Printf("Failed to open %s database: %v", db.Name(), err)
		os.Exit(exitCodeErr)
	}
	defer db.Close()

	server := NewServer(db)

	httpServer := &http.Server{
		Addr:    ":8080",
		Handler: server,
	}

	go func() {
		log.Printf("listening on %s\n", httpServer.Addr)
		if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			fmt.Fprintf(os.Stderr, "error listening and serving: %s\n", err)
		}
	}()

	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		<-ctx.Done()
		// make a new context for the Shutdown (thanks Alessandro Rosetti)
		// shutdownCtx := context.Background()
		shutdownCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
		defer cancel()
		if err := httpServer.Shutdown(shutdownCtx); err != nil {
			fmt.Fprintf(os.Stderr, "error shutting down http server: %s\n", err)
		}
		log.Printf("shut down http server on %s\n", httpServer.Addr)
	}()
	wg.Wait()
	return nil
}

// The NewServer constructor is responsible for all the top-level HTTP stuff that applies to all endpoints, like CORS, auth middleware, and logging:
func NewServer(db *sqlite.Database) http.Handler {
	mux := http.NewServeMux()

	static, err := fs.Sub(frontend, "frontend")
	if err != nil {
		log.Fatal(err)
	}

	// Print the embedded filesystem tree
	fmt.Println("Embedded frontend filesystem tree:")
	err = printFSTree(static, ".", 0)
	if err != nil {
		log.Printf("Error printing filesystem tree: %v\n", err)
	}
	fmt.Println("--- End of tree ---")

	addRoutes(
		mux,
		static,
		nil, // templ
	)


	mux.HandleFunc("GET /stream", func(w http.ResponseWriter, r *http.Request) {
		ticker := time.NewTicker(1000 * time.Millisecond)
		
		// original: defer ticker.Stop()
		
		defer func() {
			fmt.Println("defer executed")
			ticker.Stop()
		}()

		sse := datastar.NewSSE(w, r)
		for {
			select {
			case <-r.Context().Done():
				slog.Debug("Client connection closed")
				return
			case <-ticker.C:
				bytes := make([]byte, 3)

				if _, err := rand.Read(bytes); err != nil {
					slog.Error("Error generating random bytes: ", slog.String("error", err.Error()))
					return
				}
				hexString := hex.EncodeToString(bytes)
				frag := fmt.Sprintf(`<span id="feed" style="color:#%s;border:1px solid #%s;border-radius:0.25rem;padding:1rem;">%s</span>`, hexString, hexString, hexString)

				sse.MergeFragments(frag)
			}
		}
	})



	var handler http.Handler = mux

	// handler = authMiddleware(handler)
	handler = headerMiddleware(handler)

	return handler
}

// printFSTree prints a tree-like structure of the given filesystem.
func printFSTree(efs fs.FS, root string, indentLevel int) error {
	return fs.WalkDir(efs, root, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}

		// Skip the root directory itself for cleaner output if it's "."
		if path == "." && root == "." {
			return nil
		}

		indent := strings.Repeat("│   ", indentLevel)
		connector := "├── "
		// For the last item in a directory, use a different connector.
		// This requires knowing if it's the last item, which fs.WalkDir doesn't directly provide.
		// For simplicity, we'll use the same connector for all items.
		// A more sophisticated approach would involve reading directory entries first.

		fmt.Printf("%s%s%s\n", indent, connector, filepath.Base(path))

		if d.IsDir() && path != root { // Avoid infinite recursion for the root itself if not handled carefully
			// The WalkDir function handles recursion, so we don't need to call printFSTree recursively here.
			// We adjust indentLevel based on path depth for visual representation.
			// This simple indentation based on WalkDir's path might not be perfect for deep structures
			// but gives a good overview.
		}
		return nil
	})
}

// authMiddleware is a simple authentication middleware
// func authMiddleware(next http.Handler) http.Handler {
// 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 		user, pass, ok := r.BasicAuth()
// 		if !ok || !validateUser(user, pass) {
// 			w.Header().Set("WWW-Authenticate", `Basic realm="Please enter your credentials"`)
// 			http.Error(w, "Unauthorized", http.StatusUnauthorized)
// 			return
// 		}
// 		next.ServeHTTP(w, r)
// 	})
// }

// authMiddleware is a simple authentication middleware
func headerMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Request URL:", r.URL.String())
		fmt.Println("Request Headers:")
		for key, values := range r.Header {
			for _, value := range values {
				if key == "Referer" || strings.HasPrefix(key, "Hx") {

					fmt.Printf("%s: %s\n", key, value)
				}
			}
		}
		fmt.Println()
		next.ServeHTTP(w, r)
	})
}

// validateUser validates the user credentials
func validateUser(username, password string) bool {
	// In a real application, these credentials should be stored securely.
	return strings.EqualFold(username, "admin") && password == "password"
}
