package api

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"torrentGateway/internal/auth"
	"torrentGateway/internal/middleware"
	"github.com/gorilla/mux"
)

// AuthHandlers provides authentication-related HTTP handlers
type AuthHandlers struct {
	nostrAuth *auth.NostrAuth
	gateway   *Gateway
}

// NewAuthHandlers creates new authentication handlers
func NewAuthHandlers(nostrAuth *auth.NostrAuth, gateway *Gateway) *AuthHandlers {
	return &AuthHandlers{
		nostrAuth: nostrAuth,
		gateway:   gateway,
	}
}

// LoginRequest represents a login request
type LoginRequest struct {
	AuthType  string `json:"auth_type"`  // "nip07" or "nip46"
	AuthEvent string `json:"auth_event"` // For NIP-07: signed event JSON
	BunkerURL string `json:"bunker_url"` // For NIP-46: bunker connection URL
}

// LoginResponse represents a login response
type LoginResponse struct {
	Success      bool   `json:"success"`
	SessionToken string `json:"session_token,omitempty"`
	Pubkey       string `json:"pubkey,omitempty"`
	Message      string `json:"message,omitempty"`
	Challenge    string `json:"challenge,omitempty"`
}

// UserStatsResponse represents user statistics
type UserStatsResponse struct {
	Pubkey      string `json:"pubkey"`
	DisplayName string `json:"display_name,omitempty"`
	FileCount   int    `json:"file_count"`
	StorageUsed int64  `json:"storage_used"`
	LastLogin   string `json:"last_login"`
}

// UserFile represents a file in user's file list
type UserFile struct {
	Hash        string `json:"hash"`
	Name        string `json:"name"`
	Size        int64  `json:"size"`
	StorageType string `json:"storage_type"`
	AccessLevel string `json:"access_level"`
	UploadedAt  string `json:"uploaded_at"`
}

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

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

	var pubkey string
	var err error

	switch req.AuthType {
	case "nip07":
		pubkey, err = ah.nostrAuth.ValidateNIP07(req.AuthEvent)
		if err != nil {
			response := LoginResponse{
				Success: false,
				Message: fmt.Sprintf("NIP-07 validation failed: %v", err),
			}
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusUnauthorized)
			json.NewEncoder(w).Encode(response)
			return
		}

	case "nip46":
		pubkey, err = ah.nostrAuth.ValidateNIP46(req.BunkerURL)
		if err != nil {
			response := LoginResponse{
				Success: false,
				Message: fmt.Sprintf("NIP-46 validation failed: %v", err),
			}
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusUnauthorized)
			json.NewEncoder(w).Encode(response)
			return
		}

	default:
		response := LoginResponse{
			Success: false,
			Message: "Invalid auth_type: must be 'nip07' or 'nip46'",
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(response)
		return
	}

	// Create session
	sessionToken, err := ah.nostrAuth.CreateSession(pubkey)
	if err != nil {
		response := LoginResponse{
			Success: false,
			Message: fmt.Sprintf("Failed to create session: %v", err),
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(response)
		return
	}

	// Set session cookie
	cookie := &http.Cookie{
		Name:     "session_token",
		Value:    sessionToken,
		Expires:  time.Now().Add(24 * time.Hour),
		HttpOnly: true,
		Secure:   false, // Set to true in production with HTTPS
		SameSite: http.SameSiteStrictMode,
		Path:     "/",
	}
	http.SetCookie(w, cookie)

	response := LoginResponse{
		Success:      true,
		SessionToken: sessionToken,
		Pubkey:       pubkey,
		Message:      "Login successful",
	}

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

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

	// Get session token from cookie or header
	var token string
	if cookie, err := r.Cookie("session_token"); err == nil {
		token = cookie.Value
	}

	if token != "" {
		// Revoke session
		ah.nostrAuth.RevokeSession(token)
	}

	// Clear session cookie
	cookie := &http.Cookie{
		Name:     "session_token",
		Value:    "",
		Expires:  time.Now().Add(-1 * time.Hour),
		HttpOnly: true,
		Path:     "/",
	}
	http.SetCookie(w, cookie)

	response := map[string]bool{"success": true}
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// UserStatsHandler returns user statistics
func (ah *AuthHandlers) UserStatsHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	pubkey := middleware.GetUserFromContext(r.Context())
	if pubkey == "" {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	// Get user info
	user, err := ah.nostrAuth.GetUser(pubkey)
	if err != nil {
		http.Error(w, "Failed to get user info", http.StatusInternalServerError)
		return
	}

	// Calculate current stats
	storageUsed, fileCount, err := ah.gateway.storage.GetUserStats(pubkey)
	if err != nil {
		http.Error(w, "Failed to calculate stats", http.StatusInternalServerError)
		return
	}

	// Update cached stats
	ah.nostrAuth.UpdateUserStats(pubkey, storageUsed, fileCount)

	response := UserStatsResponse{
		Pubkey:      pubkey,
		FileCount:   fileCount,
		StorageUsed: storageUsed,
	}

	if user != nil {
		response.DisplayName = user.DisplayName
		response.LastLogin = user.LastLogin.Format(time.RFC3339)
	}

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

// UserFilesHandler returns user's files
func (ah *AuthHandlers) UserFilesHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	pubkey := middleware.GetUserFromContext(r.Context())
	if pubkey == "" {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	// Get user's files
	files, err := ah.gateway.storage.GetUserFiles(pubkey)
	if err != nil {
		http.Error(w, "Failed to get user files", http.StatusInternalServerError)
		return
	}

	// Convert to response format
	var userFiles []UserFile
	if files != nil {
		for _, file := range files {
			userFiles = append(userFiles, UserFile{
				Hash:        file.Hash,
				Name:        file.OriginalName,
				Size:        file.Size,
				StorageType: file.StorageType,
				AccessLevel: file.AccessLevel,
				UploadedAt:  file.CreatedAt.Format(time.RFC3339),
			})
		}
	}
	
	// Ensure we always return an array, never null
	if userFiles == nil {
		userFiles = []UserFile{}
	}

	response := struct {
		Files []UserFile `json:"files"`
	}{
		Files: userFiles,
	}
	
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// DeleteFileHandler deletes a user's file
func (ah *AuthHandlers) DeleteFileHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodDelete {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	pubkey := middleware.GetUserFromContext(r.Context())
	if pubkey == "" {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	vars := mux.Vars(r)
	fileHash := vars["hash"]

	if fileHash == "" {
		http.Error(w, "Missing file hash", http.StatusBadRequest)
		return
	}

	// Delete the file
	err := ah.gateway.storage.DeleteUserFile(fileHash, pubkey)
	if err != nil {
		if err.Error() == "file not found" {
			http.Error(w, "File not found", http.StatusNotFound)
			return
		}
		if err.Error() == "permission denied: not file owner" {
			http.Error(w, "Permission denied", http.StatusForbidden)
			return
		}
		http.Error(w, "Failed to delete file", http.StatusInternalServerError)
		return
	}

	response := map[string]interface{}{
		"success": true,
		"message": "File deleted successfully",
		"hash":    fileHash,
	}

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

// ChallengeHandler generates an authentication challenge
func (ah *AuthHandlers) ChallengeHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	challenge, err := auth.GenerateChallenge()
	if err != nil {
		http.Error(w, "Failed to generate challenge", http.StatusInternalServerError)
		return
	}

	response := map[string]string{
		"challenge": challenge,
	}

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

// UpdateFileAccessRequest represents a file access update request
type UpdateFileAccessRequest struct {
	AccessLevel string `json:"access_level"`
}

// UpdateFileAccessHandler updates a file's access level
func (ah *AuthHandlers) UpdateFileAccessHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPut {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	pubkey := middleware.GetUserFromContext(r.Context())
	if pubkey == "" {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	vars := mux.Vars(r)
	fileHash := vars["hash"]

	if fileHash == "" {
		http.Error(w, "Missing file hash", http.StatusBadRequest)
		return
	}

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

	// Validate access level
	if req.AccessLevel != "public" && req.AccessLevel != "private" {
		http.Error(w, "Invalid access level: must be 'public' or 'private'", http.StatusBadRequest)
		return
	}

	// Update the file access level
	err := ah.gateway.storage.UpdateFileAccess(fileHash, pubkey, req.AccessLevel)
	if err != nil {
		if err.Error() == "file not found" {
			http.Error(w, "File not found", http.StatusNotFound)
			return
		}
		if err.Error() == "permission denied: not file owner" {
			http.Error(w, "Permission denied", http.StatusForbidden)
			return
		}
		http.Error(w, "Failed to update file access", http.StatusInternalServerError)
		return
	}

	response := map[string]interface{}{
		"success":      true,
		"message":      "File access level updated successfully",
		"hash":         fileHash,
		"access_level": req.AccessLevel,
	}

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

// AdminStatusHandler checks if the authenticated user is an admin
func (ah *AuthHandlers) AdminStatusHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	pubkey := middleware.GetUserFromContext(r.Context())
	if pubkey == "" {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	// Check if user is admin - this would depend on your admin config
	// For now, we'll check against the config admin pubkeys
	isAdmin := false
	if ah.gateway.config.Admin.Enabled {
		for _, adminPubkey := range ah.gateway.config.Admin.Pubkeys {
			if adminPubkey == pubkey {
				isAdmin = true
				break
			}
		}
	}

	response := map[string]interface{}{
		"is_admin": isAdmin,
		"pubkey":   pubkey,
	}

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