package main

import (
	"embed"
	"log"
	"mime"
	"net/http"
	"path"
	"path/filepath"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/glebarez/sqlite"
	"gorm.io/gorm"

	"qn/notes"
)

func serveStaticFile(c *gin.Context, prefix string) error {
	cleanPath := path.Clean(c.Request.URL.Path)
	if cleanPath == "/" {
		cleanPath = "/index.html"
	}

	// Try to read the exact file first
	content, err := frontend.ReadFile(prefix + cleanPath)
	ext := strings.ToLower(filepath.Ext(cleanPath))

	// If file not found OR the path has no extension (likely a route path), serve index.html
	if err != nil || ext == "" {
		content, err = frontend.ReadFile(prefix + "/index.html")
		if err != nil {
			return err
		}
		c.Header("Content-Type", "text/html; charset=utf-8")
		// Add security headers for HTML content
		c.Header("X-Content-Type-Options", "nosniff")
		c.Data(http.StatusOK, "text/html; charset=utf-8", content)
		return nil
	}

	// For actual files, set the correct MIME type
	var mimeType string
	switch ext {
	case ".js":
		mimeType = "application/javascript; charset=utf-8"
	case ".css":
		mimeType = "text/css; charset=utf-8"
	case ".html":
		mimeType = "text/html; charset=utf-8"
	case ".json":
		mimeType = "application/json; charset=utf-8"
	case ".png":
		mimeType = "image/png"
	case ".svg":
		mimeType = "image/svg+xml"
	default:
		mimeType = mime.TypeByExtension(ext)
		if mimeType == "" {
			// Try to detect content type from the content itself
			mimeType = http.DetectContentType(content)
		}
	}

	// Set security headers for all responses
	c.Header("X-Content-Type-Options", "nosniff")
	c.Data(http.StatusOK, mimeType, content)
	return nil
}

//go:embed frontend/build/* frontend/static/*
var frontend embed.FS

func main() {
	// Initialize database
	db, err := gorm.Open(sqlite.Open("notes.db"), &gorm.Config{})
	if err != nil {
		log.Fatal(err)
	}

	// Auto migrate the schema
	if err := db.AutoMigrate(&notes.Note{}, &notes.NoteLink{}); err != nil {
		log.Fatal(err)
	}

	// Initialize services
	noteService := notes.NewService(db)
	noteHandler := notes.NewHandler(noteService)

	// Create Gin router
	r := gin.Default()

	// Trust only loopback addresses
	if err := r.SetTrustedProxies([]string{"127.0.0.1", "::1"}); err != nil {
		log.Fatal(err)
	}

	// API routes
	api := r.Group("/api")
	{
		noteHandler.RegisterRoutes(api)
		// TODO: Add feeds and links routes when implemented
	}

	// Serve frontend
	r.NoRoute(handleFrontend)

	log.Printf("INFO: Server starting on http://localhost:3000")
	log.Fatal(r.Run(":3000"))
}

func handleFrontend(c *gin.Context) {
	// Don't serve API routes
	if path.Dir(c.Request.URL.Path) == "/api" {
		c.Status(http.StatusNotFound)
		return
	}

	err := serveStaticFile(c, "frontend/build")
	if err != nil { // if serveStaticFile returns an error, it has already tried to serve index.html as fallback
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
	}
}