package main

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"golang.org/x/crypto/bcrypt"
)

// UserStore manages user data
type UserStore struct {
	mu       sync.RWMutex
	Users    map[string]*User // key: user ID
	Usernames map[string]string // key: username, value: user ID
	Emails   map[string]string // key: email, value: user ID
	Sessions map[string]string // key: session token, value: user ID
	filePath string
}

var userStore *UserStore

// InitUserStore initializes the user store
func InitUserStore() {
	userStore = &UserStore{
		Users:     make(map[string]*User),
		Usernames: make(map[string]string),
		Emails:    make(map[string]string),
		Sessions:  make(map[string]string),
		filePath:  "./files/users.json",
	}

	// Create files directory if it doesn't exist
	os.MkdirAll("./files", 0755)

	// Load existing users
	userStore.loadFromFile()
}

// loadFromFile loads users from JSON file
func (us *UserStore) loadFromFile() error {
	us.mu.Lock()
	defer us.mu.Unlock()

	data, err := os.ReadFile(us.filePath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil // File doesn't exist yet, that's ok
		}
		return err
	}

	var storageUsers []*userStorage
	if err := json.Unmarshal(data, &storageUsers); err != nil {
		return err
	}

	// Convert from storage format to User struct and rebuild indexes
	for _, su := range storageUsers {
		user := &User{
			ID:        su.ID,
			Username:  su.Username,
			Email:     su.Email,
			Password:  su.Password, // Restore password
			CreatedAt: su.CreatedAt,
			UpdatedAt: su.UpdatedAt,
		}
		us.Users[user.ID] = user
		us.Usernames[strings.ToLower(user.Username)] = user.ID
		us.Emails[strings.ToLower(user.Email)] = user.ID
	}

	return nil
}

// userStorage is used for saving/loading users with passwords
type userStorage struct {
	ID        string    `json:"id"`
	Username  string    `json:"username"`
	Email     string    `json:"email"`
	Password  string    `json:"password"` // Include password for storage
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

// saveToFile saves users to JSON file
// NOTE: Caller must hold the lock!
func (us *UserStore) saveToFile() error {
	// Convert map to slice of storage struct (with passwords)
	users := make([]*userStorage, 0, len(us.Users))
	for _, user := range us.Users {
		users = append(users, &userStorage{
			ID:        user.ID,
			Username:  user.Username,
			Email:     user.Email,
			Password:  user.Password,
			CreatedAt: user.CreatedAt,
			UpdatedAt: user.UpdatedAt,
		})
	}

	data, err := json.MarshalIndent(users, "", "  ")
	if err != nil {
		return err
	}

	return os.WriteFile(us.filePath, data, 0644)
}

// generateID generates a random ID
func generateID() string {
	bytes := make([]byte, 16)
	rand.Read(bytes)
	return hex.EncodeToString(bytes)
}

// generateToken generates a session token
func generateToken() string {
	bytes := make([]byte, 32)
	rand.Read(bytes)
	return hex.EncodeToString(bytes)
}

// CreateUser creates a new user
func (us *UserStore) CreateUser(username, email, password string) (*User, error) {
	// Hash password BEFORE acquiring lock (this is slow!)
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		return nil, err
	}

	us.mu.Lock()
	defer us.mu.Unlock()

	// Check if username already exists
	if _, exists := us.Usernames[strings.ToLower(username)]; exists {
		return nil, fmt.Errorf("username already exists")
	}

	// Check if email already exists
	if _, exists := us.Emails[strings.ToLower(email)]; exists {
		return nil, fmt.Errorf("email already exists")
	}

	// Create user
	user := &User{
		ID:        generateID(),
		Username:  username,
		Email:     email,
		Password:  string(hashedPassword),
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	// Store user
	us.Users[user.ID] = user
	us.Usernames[strings.ToLower(username)] = user.ID
	us.Emails[strings.ToLower(email)] = user.ID

	// Save to file
	if err := us.saveToFile(); err != nil {
		return nil, err
	}

	return user, nil
}

// AuthenticateUser authenticates a user by username and password
func (us *UserStore) AuthenticateUser(username, password string) (*User, string, error) {
	us.mu.RLock()
	userID, exists := us.Usernames[strings.ToLower(username)]
	us.mu.RUnlock()

	if !exists {
		return nil, "", fmt.Errorf("invalid username or password")
	}

	us.mu.RLock()
	user := us.Users[userID]
	us.mu.RUnlock()

	// Compare password
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
		return nil, "", fmt.Errorf("invalid username or password")
	}

	// Generate session token
	token := generateToken()
	us.mu.Lock()
	us.Sessions[token] = user.ID
	us.mu.Unlock()

	return user, token, nil
}

// GetUserByToken retrieves a user by session token
func (us *UserStore) GetUserByToken(token string) (*User, error) {
	us.mu.RLock()
	defer us.mu.RUnlock()

	userID, exists := us.Sessions[token]
	if !exists {
		return nil, fmt.Errorf("invalid session token")
	}

	user, exists := us.Users[userID]
	if !exists {
		return nil, fmt.Errorf("user not found")
	}

	return user, nil
}

// Logout removes a session token
func (us *UserStore) Logout(token string) {
	us.mu.Lock()
	defer us.mu.Unlock()
	delete(us.Sessions, token)
}

// HTTP Handlers

// HandleRegister handles user registration
func HandleRegister(w http.ResponseWriter, r *http.Request) {
	fmt.Printf("[API] Register request received from %s\n", r.RemoteAddr)
	
	if r.Method != http.MethodPost {
		fmt.Printf("[API] Register: Method not allowed: %s\n", r.Method)
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	fmt.Printf("[API] Reading request body...\n")
	body, err := io.ReadAll(r.Body)
	if err != nil {
		fmt.Printf("[API] Error reading body: %v\n", err)
		http.Error(w, "Error reading request body", http.StatusBadRequest)
		return
	}
	defer r.Body.Close()
	fmt.Printf("[API] Body received: %s\n", string(body))

	var req RegisterRequest
	if err := json.Unmarshal(body, &req); err != nil {
		fmt.Printf("[API] Error parsing JSON: %v\n", err)
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}
	fmt.Printf("[API] Parsed: username=%s, email=%s\n", req.Username, req.Email)

	// Validate input
	if req.Username == "" || req.Email == "" || req.Password == "" {
		fmt.Printf("[API] Validation failed: missing fields\n")
		http.Error(w, "All fields are required", http.StatusBadRequest)
		return
	}

	if len(req.Password) < 6 {
		fmt.Printf("[API] Validation failed: password too short\n")
		http.Error(w, "Password must be at least 6 characters", http.StatusBadRequest)
		return
	}

	// Create user
	fmt.Printf("[API] Creating user...\n")
	user, err := userStore.CreateUser(req.Username, req.Email, req.Password)
	if err != nil {
		fmt.Printf("[API] Error creating user: %v\n", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	fmt.Printf("[API] User created successfully! ID=%s\n", user.ID)

	// Generate session token
	token := generateToken()
	userStore.mu.Lock()
	userStore.Sessions[token] = user.ID
	userStore.mu.Unlock()

	// Initialize user's favorite playlist
	if playlistManager != nil {
		playlistManager.InitializeUserPlaylists(user.ID)
	}

	fmt.Printf("[Info] User registered: %s\n", user.Username)

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(AuthResponse{
		Token: token,
		User:  *user,
	})
}

// HandleLogin handles user login
func HandleLogin(w http.ResponseWriter, r *http.Request) {
	fmt.Printf("[API] Login request received from %s\n", r.RemoteAddr)
	
	if r.Method != http.MethodPost {
		fmt.Printf("[API] Login: Method not allowed: %s\n", r.Method)
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	fmt.Printf("[API] Reading login body...\n")
	body, err := io.ReadAll(r.Body)
	if err != nil {
		fmt.Printf("[API] Error reading body: %v\n", err)
		http.Error(w, "Error reading request body", http.StatusBadRequest)
		return
	}
	defer r.Body.Close()
	fmt.Printf("[API] Login body: %s\n", string(body))

	var req LoginRequest
	if err := json.Unmarshal(body, &req); err != nil {
		fmt.Printf("[API] Error parsing JSON: %v\n", err)
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}
	fmt.Printf("[API] Login username: %s\n", req.Username)

	// Authenticate user
	fmt.Printf("[API] Authenticating...\n")
	user, token, err := userStore.AuthenticateUser(req.Username, req.Password)
	fmt.Printf("[API] Authentication result: %v\n", err)
	if err != nil {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}

	fmt.Printf("[Info] User logged in: %s\n", user.Username)

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(AuthResponse{
		Token: token,
		User:  *user,
	})
}

// HandleLogout handles user logout
func HandleLogout(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	token := r.Header.Get("Authorization")
	if token == "" {
		http.Error(w, "Missing authorization token", http.StatusUnauthorized)
		return
	}

	// Remove "Bearer " prefix if present
	token = strings.TrimPrefix(token, "Bearer ")

	userStore.Logout(token)

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

// HandleGetCurrentUser returns current user info
func HandleGetCurrentUser(w http.ResponseWriter, r *http.Request) {
	token := r.Header.Get("Authorization")
	if token == "" {
		http.Error(w, "Missing authorization token", http.StatusUnauthorized)
		return
	}

	// Remove "Bearer " prefix if present
	token = strings.TrimPrefix(token, "Bearer ")

	user, err := userStore.GetUserByToken(token)
	if err != nil {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}

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

// AuthMiddleware authenticates requests
func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// 1. 尝试从 Authorization Header 获取
		token := r.Header.Get("Authorization")
		if token != "" {
			token = strings.TrimPrefix(token, "Bearer ")
			user, err := userStore.GetUserByToken(token)
			if err == nil {
				r.Header.Set("X-User-ID", user.ID)
				next(w, r)
				return
			}
		}

		// 2. 尝试从 X-Device-Token 获取 (ESP32设备)
		deviceToken := r.Header.Get("X-Device-Token")
		if deviceToken != "" {
			// 这里需要调用DeviceManager，但为了避免循环依赖（如果user包独立），我们使用全局函数或接口
			// 假设GetDeviceManager()是全局可用的
			dm := GetDeviceManager()
			device, err := dm.VerifyToken(deviceToken)
			if err == nil {
				// 找到了设备，现在需要找到对应的用户
				// Device结构体中有Username，我们需要根据Username找到User对象
				userID := userStore.GetUserIDByUsername(device.Username)
				if userID != "" {
					r.Header.Set("X-User-ID", userID)
					
					// 更新设备最后在线时间
					dm.UpdateLastSeen(device.MAC)
					
					next(w, r)
					return
				}
			}
		}

		// 3. 尝试从 Cookie 获取 (Web Session)
		cookie, err := r.Cookie("session_token")
		if err == nil {
			username := userStore.GetUsernameByToken(cookie.Value)
			if username != "" {
				userID := userStore.GetUserIDByUsername(username)
				if userID != "" {
					r.Header.Set("X-User-ID", userID)
					next(w, r)
					return
				}
			}
		}

		http.Error(w, "Invalid or expired token", http.StatusUnauthorized)
	}
}

// GetUserStore 返回UserStore单例
func GetUserStore() *UserStore {
	return userStore
}

// GetUserIDByUsername returns user ID by username
func (us *UserStore) GetUserIDByUsername(username string) string {
	us.mu.RLock()
	defer us.mu.RUnlock()

	return us.Usernames[username]
}

// GetUsernameByToken 通过session token获取用户名
func (us *UserStore) GetUsernameByToken(token string) string {
	us.mu.RLock()
	defer us.mu.RUnlock()

	userID, exists := us.Sessions[token]
	if !exists {
		return ""
	}

	user, exists := us.Users[userID]
	if !exists {
		return ""
	}

	return user.Username
}
