package main

import (
	"context"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"SimpleArmaAdmin/internal/auth"
	internal_nats "SimpleArmaAdmin/internal/nats"
	"SimpleArmaAdmin/internal/webauthn"
	"github.com/gorilla/websocket"
	"github.com/nats-io/nats.go"
)

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool { return true },
}

type Gateway struct {
	natsClient *internal_nats.Client
	dashboards map[*websocket.Conn]bool
	mu         sync.Mutex
}

func (g *Gateway) handleWebSocket(w http.ResponseWriter, r *http.Request) {
	role := r.URL.Query().Get("role")
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("Upgrade error: %v", err)
		return
	}

	if role == "dashboard" {
		g.mu.Lock()
		g.dashboards[conn] = true
		g.mu.Unlock()
	}

	defer func() {
		if role == "dashboard" {
			g.mu.Lock()
			delete(g.dashboards, conn)
			g.mu.Unlock()
		}
		conn.Close()
	}()

	communityID := "comm-123-abc"

	for {
		messageType, p, err := conn.ReadMessage()
		if err != nil {
			break
		}

		if role != "dashboard" {
			if messageType == websocket.BinaryMessage {
				g.natsClient.PublishLog(context.Background(), communityID, "live", p)
			} else {
				g.natsClient.Conn.Publish("telemetry."+communityID, p)
			}
		}
	}
}

func (g *Gateway) broadcast(messageType int, data []byte) {
	g.mu.Lock()
	defer g.mu.Unlock()
	for client := range g.dashboards {
		err := client.WriteMessage(messageType, data)
		if err != nil {
			client.Close()
			delete(g.dashboards, client)
		}
	}
}

// ============================================================================
// REGISTRATION HANDLERS
// ============================================================================

// handleRegister - Password-based registration
func (g *Gateway) handleRegister(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		Username      string `json:"username"`
		Email         string `json:"email"`
		Password      string `json:"password"`
		CommunityName string `json:"communityName"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	// Validate password strength
	if err := auth.ValidatePasswordStrength(req.Password); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
		return
	}

	// Hash password (validates it can be hashed)
	if _, err := auth.HashPassword(req.Password); err != nil {
		http.Error(w, "Failed to hash password", http.StatusInternalServerError)
		return
	}

	// TODO: Store in database
	// - Create community
	// - Create admin_user with password_hash (use the hash above)
	// - Generate master key and wrap it

	userID := "user-" + req.Username
	communityID := "comm-" + req.Username

	// Generate session token
	token, err := auth.GenerateJWT(userID, communityID, req.Username, 7*24*time.Hour)
	if err != nil {
		http.Error(w, "Failed to generate token", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"status":      "success",
		"token":       token,
		"userId":      userID,
		"communityId": communityID,
		"username":    req.Username,
		"masterKey":   "this-is-a-32-byte-master-key-xyz", // TODO: Generate real key
	})
}

// handleRegisterPasskeyBegin - Start Passkey registration
func (g *Gateway) handleRegisterPasskeyBegin(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Username    string `json:"username"`
		DisplayName string `json:"displayName"`
		Email       string `json:"email"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	options, err := webauthn.CreateRegistrationOptions(
		"user-"+req.Username,
		req.Username,
		req.DisplayName,
		"ArmaAdmin Zero-Knowledge Cloud",
		"localhost", // TODO: Load from env
	)

	if err != nil {
		http.Error(w, "Failed to create options", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(options)
}

// handleRegisterPasskeyFinish - Complete Passkey registration
func (g *Gateway) handleRegisterPasskeyFinish(w http.ResponseWriter, r *http.Request) {
	// TODO: Verify credential and store in database
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"status":  "success",
		"message": "Passkey registered successfully",
	})
}

// ============================================================================
// LOGIN HANDLERS
// ============================================================================

// handleLoginPassword - Password-based login
func (g *Gateway) handleLoginPassword(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	// TODO: Fetch user from database and verify password
	// For DEMO mode: Accept any password (bypass authentication)
	log.Printf("[DEMO] Login attempt for user: %s (auto-accepting)", req.Username)

	userID := "user-" + req.Username
	communityID := "comm-" + req.Username

	// Generate JWT
	token, err := auth.GenerateJWT(userID, communityID, req.Username, 7*24*time.Hour)
	if err != nil {
		http.Error(w, "Failed to generate token", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"status":      "success",
		"token":       token,
		"userId":      userID,
		"communityId": communityID,
		"username":    req.Username,
		"masterKey":   "this-is-a-32-byte-master-key-xyz",
	})
}

// handleLoginPasskeyBegin - Start Passkey authentication
func (g *Gateway) handleLoginPasskeyBegin(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Username string `json:"username"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	// TODO: Fetch user's credentials from DB
	options, err := webauthn.CreateAuthenticationOptions(
		"localhost", // TODO: Load from env
		[]string{},  // TODO: Fetch credential IDs
	)

	if err != nil {
		http.Error(w, "Failed to create options", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(options)
}

// handleLoginPasskeyFinish - Complete Passkey authentication
func (g *Gateway) handleLoginPasskeyFinish(w http.ResponseWriter, r *http.Request) {
	// TODO: Verify signature and create session
	userID := "user-demo"
	communityID := "comm-demo"
	username := "demo"

	token, err := auth.GenerateJWT(userID, communityID, username, 7*24*time.Hour)
	if err != nil {
		http.Error(w, "Failed to generate token", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"status":      "success",
		"token":       token,
		"userId":      userID,
		"communityId": communityID,
		"username":    username,
		"masterKey":   "this-is-a-32-byte-master-key-xyz",
	})
}

// ============================================================================
// SESSION MANAGEMENT
// ============================================================================

// handleLogout - Invalidate session
func (g *Gateway) handleLogout(w http.ResponseWriter, r *http.Request) {
	// Extract token from Authorization header
	authHeader := r.Header.Get("Authorization")
	if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
		http.Error(w, "Missing token", http.StatusUnauthorized)
		return
	}

	// TODO: Invalidate session in database

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{"status": "logged out"})
}

// handleMe - Get current user info
func (g *Gateway) handleMe(w http.ResponseWriter, r *http.Request) {
	authHeader := r.Header.Get("Authorization")
	if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
		http.Error(w, "Missing token", http.StatusUnauthorized)
		return
	}

	token := strings.TrimPrefix(authHeader, "Bearer ")
	claims, err := auth.VerifyJWT(token)
	if err != nil {
		http.Error(w, "Invalid token", http.StatusUnauthorized)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"userId":      claims.UserID,
		"username":    claims.Username,
		"communityId": claims.CommunityID,
	})
}

// Player Search Handler (Blind Index)
func (g *Gateway) handlePlayerSearch(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query().Get("q")
	if query == "" {
		http.Error(w, "Missing query parameter", http.StatusBadRequest)
		return
	}

	// TODO: Search using blind index
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"results": []map[string]string{},
	})
}

// DSGVO Export Handler
func (g *Gateway) handleDSGVOExport(w http.ResponseWriter, r *http.Request) {
	playerID := r.URL.Query().Get("playerId")
	if playerID == "" {
		http.Error(w, "Missing playerId", http.StatusBadRequest)
		return
	}

	// TODO: Fetch all encrypted logs for this player via blind index
	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Content-Disposition", "attachment; filename=player-data.json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"playerId":   playerID,
		"exportDate": "2026-04-30",
		"logs":       []interface{}{},
	})
}

// DSGVO Delete Handler
func (g *Gateway) handleDSGVODelete(w http.ResponseWriter, r *http.Request) {
	var req struct {
		PlayerID string `json:"playerId"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	// TODO: Delete all records matching the blind index
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{"status": "deleted"})
}

// Shared Hosting Ingestion Endpoint
func (g *Gateway) handleIngest(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	apiKey := r.Header.Get("X-API-Key")
	if apiKey == "" {
		http.Error(w, "Missing API key", http.StatusUnauthorized)
		return
	}

	// TODO: Validate API key against database

	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Failed to read body", http.StatusBadRequest)
		return
	}

	// Forward to NATS for storage
	communityID := "comm-123-abc" // TODO: Get from API key
	g.natsClient.PublishLog(context.Background(), communityID, "api", body)

	w.WriteHeader(http.StatusAccepted)
	w.Write([]byte("OK"))
}

func main() {
	natsURL := os.Getenv("NATS_URL")
	if natsURL == "" {
		natsURL = "nats://localhost:4222"
	}

	nc, err := internal_nats.Connect(natsURL)
	if err != nil {
		log.Fatalf("NATS error: %v", err)
	}
	defer nc.Close()

	nc.SetupStream(context.Background(), "LOGS", []string{"logs.>"})

	gateway := &Gateway{
		natsClient: nc,
		dashboards: make(map[*websocket.Conn]bool),
	}

	// Listen to NATS and push to Dashboards ONLY
	go func() {
		nc.Conn.Subscribe("logs.>", func(m *nats.Msg) {
			gateway.broadcast(websocket.BinaryMessage, m.Data)
		})
		nc.Conn.Subscribe("telemetry.>", func(m *nats.Msg) {
			gateway.broadcast(websocket.TextMessage, m.Data)
		})
	}()

	// WebSocket endpoint
	http.HandleFunc("/ws", gateway.handleWebSocket)

	// Registration endpoints (Password + Passkey)
	http.HandleFunc("/api/auth/register", gateway.handleRegister)
	http.HandleFunc("/api/auth/register/passkey/begin", gateway.handleRegisterPasskeyBegin)
	http.HandleFunc("/api/auth/register/passkey/finish", gateway.handleRegisterPasskeyFinish)

	// Login endpoints (Password + Passkey)
	http.HandleFunc("/api/auth/login/password", gateway.handleLoginPassword)
	http.HandleFunc("/api/auth/login/passkey/begin", gateway.handleLoginPasskeyBegin)
	http.HandleFunc("/api/auth/login/passkey/finish", gateway.handleLoginPasskeyFinish)

	// Session management
	http.HandleFunc("/api/auth/logout", gateway.handleLogout)
	http.HandleFunc("/api/auth/me", gateway.handleMe)

	// Player roster & DSGVO endpoints
	http.HandleFunc("/api/players/search", gateway.handlePlayerSearch)
	http.HandleFunc("/api/dsgvo/export", gateway.handleDSGVOExport)
	http.HandleFunc("/api/dsgvo/delete", gateway.handleDSGVODelete)

	// Shared hosting endpoint (for Nitrado customers)
	http.HandleFunc("/api/ingest", gateway.handleIngest)

	log.Println("Gateway listening on :8080 (Full MVP)")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
