package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"direct-dev.ru/gitea/GiteaAdmin/elowdb-go/pkg/linedb"
)

// User представляет пользователя
type User struct {
	ID        int    `json:"id"`
	Username  string `json:"username"`
	Email     string `json:"email"`
	IsActive  bool   `json:"isActive"`
	Role      string `json:"role"`
	CreatedAt int64  `json:"createdAt"`
	LastLogin *int64 `json:"lastLogin,omitempty"`
}

// Product представляет продукт
type Product struct {
	ID        int     `json:"id"`
	Name      string  `json:"name"`
	Price     float64 `json:"price"`
	Category  string  `json:"category"`
	InStock   bool    `json:"inStock"`
	SellerID  int     `json:"sellerId"`
	CreatedAt int64   `json:"createdAt"`
}

// Order представляет заказ
type Order struct {
	ID         int     `json:"id"`
	UserID     int     `json:"userId"`
	ProductID  int     `json:"productId"`
	Quantity   int     `json:"quantity"`
	TotalPrice float64 `json:"totalPrice"`
	Status     string  `json:"status"`
	CreatedAt  int64   `json:"createdAt"`
	UpdatedAt  int64   `json:"updatedAt"`
}

// OrderItem представляет элемент заказа
type OrderItem struct {
	ID        int     `json:"id"`
	OrderID   int     `json:"orderId"`
	ProductID int     `json:"productId"`
	Quantity  int     `json:"quantity"`
	Price     float64 `json:"price"`
}

// APIResponse представляет ответ API
type APIResponse struct {
	Success bool        `json:"success"`
	Data    interface{} `json:"data,omitempty"`
	Error   string      `json:"error,omitempty"`
	Message string      `json:"message,omitempty"`
}

var Database *linedb.LineDb
var server *http.Server

// parseTestNumbers парсит номера тестов из аргументов командной строки
func parseTestNumbers() []int {
	if len(os.Args) < 2 {
		return []int{}
	}

	// Получаем аргумент после имени программы
	arg := os.Args[1]

	// Проверяем, что это не флаг помощи
	if arg == "-h" || arg == "--help" || arg == "help" {
		printUsage()
		return []int{}
	}

	// Разбиваем по запятой
	parts := strings.Split(arg, ",")
	var numbers []int

	for _, part := range parts {
		part = strings.TrimSpace(part)
		if part == "" {
			continue
		}

		num, err := strconv.Atoi(part)
		if err != nil {
			fmt.Printf("Ошибка: '%s' не является числом\n", part)
			printUsage()
			return []int{}
		}

		// Проверяем диапазон
		if num < 1 || num > 4 {
			fmt.Printf("Ошибка: номер теста %d должен быть от 1 до 4\n", num)
			printUsage()
			return []int{}
		}

		numbers = append(numbers, num)
	}

	return numbers
}

// printUsage выводит справку по использованию
func printUsage() {
	fmt.Println("\nИспользование:")
	fmt.Println("  go run main.go                    # Запустить все тесты")
	fmt.Println("  go run main.go 1                  # Запустить только тест 1")
	fmt.Println("  go run main.go 1,3                # Запустить тесты 1 и 3")
	fmt.Println("  go run main.go 2,3,4              # Запустить тесты 2, 3 и 4")
	fmt.Println("  go run main.go help               # Показать эту справку")
	fmt.Println("\nДоступные тесты:")
	fmt.Println("  1 - Базовые CRUD операции")
	fmt.Println("  2 - Сложные запросы и фильтрация")
	fmt.Println("  3 - Работа с несколькими коллекциями")
	fmt.Println("  4 - Тест производительности")
	fmt.Println("  5 - HTTP API сервер")
	fmt.Println("  6 - API endpoints")
	os.Exit(0)
}

// runAllTests запускает все тесты
func runAllTests() {
	testBasicCRUDOperations()
	testComplexQueries()
	testMultipleCollections()
	testPerformance()
	testHTTPServer()
	testAPIEndpoints()
}

// runSelectedTests запускает только выбранные тесты
func runSelectedTests(numbers []int) {
	for _, num := range numbers {
		switch num {
		case 1:
			fmt.Println("1. Базовые CRUD операции")
			testBasicCRUDOperations()
		case 2:
			fmt.Println("2. Сложные запросы и фильтрация")
			testComplexQueries()
		case 3:
			fmt.Println("3. Работа с несколькими коллекциями")
			testMultipleCollections()
		case 4:
			fmt.Println("4. Тест производительности")
			testPerformance()
		case 5:
			fmt.Println("5. HTTP API сервер")
			testHTTPServer()
		case 6:
			fmt.Println("6. API endpoints")
			testAPIEndpoints()
		}
	}
}

func testBasicCRUDOperations() {
	// Создаем пользователя
	user := map[string]any{
		"id":        1,
		"username":  "testuser",
		"email":     "test@example.com",
		"isActive":  true,
		"role":      "user",
		"createdAt": time.Now().Unix(),
	}

	// Вставка
	if err := Database.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil {
		log.Printf("Failed to insert user: %v", err)
		return
	}
	fmt.Printf("\tПользователь создан: %s\n", user["username"])

	// Чтение
	result, err := Database.Read("users", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to read users: %v", err)
		return
	}
	fmt.Printf("\tПользователей в базе: %d\n", len(result))

	// Обновление
	updateData := map[string]any{"role": "admin"}
	updated, err := Database.Update(updateData, "users", map[string]any{"id": 1}, linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to update user: %v", err)
		return
	}
	fmt.Printf("   Обновлено пользователей: %d\n", len(updated))

	// Удаление
	deleted, err := Database.Delete(map[string]any{"id": 1}, "users", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to delete user: %v", err)
		return
	}
	fmt.Printf("   Удалено пользователей: %d\n", len(deleted))

	// Проверяем, что пользователь удален
	result, err = Database.Read("users", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to read users after delete: %v", err)
		return
	}
	fmt.Printf("   Пользователей после удаления: %d\n", len(result))
}

func testComplexQueries() {
	// Создаем несколько пользователей
	users := []any{
		map[string]any{
			"id":        1,
			"username":  "john_doe",
			"email":     "john@example.com",
			"isActive":  true,
			"role":      "user",
			"createdAt": time.Now().Unix(),
		},
		map[string]any{
			"id":        2,
			"username":  "jane_smith",
			"email":     "jane@example.com",
			"isActive":  true,
			"role":      "admin",
			"createdAt": time.Now().Unix(),
		},
		map[string]any{
			"id":        3,
			"username":  "bob_wilson",
			"email":     "bob@example.com",
			"isActive":  false,
			"role":      "user",
			"createdAt": time.Now().Unix(),
		},
	}

	if err := Database.Insert(users, "users", linedb.LineDbAdapterOptions{}); err != nil {
		log.Printf("Failed to insert users: %v", err)
		return
	}

	// Фильтрация по роли
	admins, err := Database.ReadByFilter(map[string]any{"role": "admin"}, "users", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to filter admins: %v", err)
		return
	}
	fmt.Printf("   Администраторов: %d\n", len(admins))

	// Фильтрация по активности
	activeUsers, err := Database.ReadByFilter(map[string]any{"isActive": true}, "users", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to filter active users: %v", err)
		return
	}
	fmt.Printf("   Активных пользователей: %d\n", len(activeUsers))

	// Строковая фильтрация
	emailFilter, err := Database.ReadByFilter("email == 'john@example.com'", "users", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to filter by email: %v", err)
		return
	}
	fmt.Printf("   Пользователей с email john@example.com: %d\n", len(emailFilter))

	// Частичное совпадение
	partialMatch, err := Database.ReadByFilter(
		map[string]any{"username": "john"},
		"users",
		linedb.LineDbAdapterOptions{StrictCompare: false},
	)
	if err != nil {
		log.Printf("Failed to partial match: %v", err)
		return
	}
	fmt.Printf("   Пользователей с именем содержащим 'john': %d\n", len(partialMatch))
}

func testMultipleCollections() {
	// Создаем пользователя
	user := map[string]any{
		"id":        1,
		"username":  "customer1",
		"email":     "customer1@example.com",
		"isActive":  true,
		"role":      "customer",
		"createdAt": time.Now().Unix(),
	}

	// Создаем продукты
	products := []any{
		map[string]any{
			"id":        1,
			"name":      "Laptop",
			"price":     999.99,
			"category":  "Electronics",
			"inStock":   true,
			"sellerId":  1,
			"createdAt": time.Now().Unix(),
		},
		map[string]any{
			"id":        2,
			"name":      "Mouse",
			"price":     29.99,
			"category":  "Electronics",
			"inStock":   true,
			"sellerId":  1,
			"createdAt": time.Now().Unix(),
		},
	}

	// Создаем заказ
	order := map[string]any{
		"id":         1,
		"userId":     1,
		"productId":  1,
		"quantity":   2,
		"totalPrice": 1999.98,
		"status":     "pending",
		"createdAt":  time.Now().Unix(),
		"updatedAt":  time.Now().Unix(),
	}

	// Создаем элементы заказа
	orderItems := []any{
		map[string]any{
			"id":        1,
			"orderId":   1,
			"productId": 1,
			"quantity":  2,
			"price":     999.99,
		},
	}

	// Вставляем данные во все коллекции
	if err := Database.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil {
		log.Printf("Failed to insert user: %v", err)
		return
	}

	if err := Database.Insert(products, "products", linedb.LineDbAdapterOptions{}); err != nil {
		log.Printf("Failed to insert products: %v", err)
		return
	}

	if err := Database.Insert(order, "orders", linedb.LineDbAdapterOptions{}); err != nil {
		log.Printf("Failed to insert order: %v", err)
		return
	}

	if err := Database.Insert(orderItems, "orderItems", linedb.LineDbAdapterOptions{}); err != nil {
		log.Printf("Failed to insert order items: %v", err)
		return
	}

	// Читаем данные из всех коллекций
	users, err := Database.Read("users", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to read users: %v", err)
		return
	}

	productsData, err := Database.Read("products", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to read products: %v", err)
		return
	}

	orders, err := Database.Read("orders", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to read orders: %v", err)
		return
	}

	orderItemsData, err := Database.Read("orderItems", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to read order items: %v", err)
		return
	}

	fmt.Printf("   Пользователей: %d\n", len(users))
	fmt.Printf("   Продуктов: %d\n", len(productsData))
	fmt.Printf("   Заказов: %d\n", len(orders))
	fmt.Printf("   Элементов заказов: %d\n", len(orderItemsData))

	// Сложный запрос: найти все заказы пользователя
	userOrders, err := Database.ReadByFilter(map[string]any{"userId": 1}, "orders", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to get user orders: %v", err)
		return
	}
	fmt.Printf("   Заказов пользователя 1: %d\n", len(userOrders))
}

func testPerformance() {
	// Тест производительности массовой вставки
	recordCount := 500
	fmt.Printf("   Создание %d записей для теста производительности...\n", recordCount)

	// Создаем пользователей
	users := make([]any, recordCount)
	for i := 1; i <= recordCount; i++ {
		users[i-1] = map[string]any{
			"id":        i,
			"username":  fmt.Sprintf("user%d", i),
			"email":     fmt.Sprintf("user%d@example.com", i),
			"isActive":  i%2 == 0, // чередуем активных и неактивных
			"role":      "user",
			"createdAt": time.Now().Unix(),
		}
	}

	// Измеряем время вставки
	startTime := time.Now()
	if err := Database.Insert(users, "users", linedb.LineDbAdapterOptions{}); err != nil {
		log.Printf("Failed to insert users: %v", err)
		return
	}
	endTime := time.Now()

	duration := endTime.Sub(startTime)
	fmt.Printf("   Вставлено пользователей: %d\n", recordCount)
	fmt.Printf("   Время выполнения: %v\n", duration)
	fmt.Printf("   Скорость: %.2f записей/сек\n", float64(recordCount)/duration.Seconds())

	// Тест производительности чтения
	startTime = time.Now()
	result, err := Database.Read("users", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to read users: %v", err)
		return
	}
	endTime = time.Now()

	readDuration := endTime.Sub(startTime)
	fmt.Printf("   Прочитано пользователей: %d\n", len(result))
	fmt.Printf("   Время чтения: %v\n", readDuration)
	fmt.Printf("   Скорость чтения: %.2f записей/сек\n", float64(len(result))/readDuration.Seconds())

	// Тест производительности фильтрации
	startTime = time.Now()
	activeUsers, err := Database.ReadByFilter(map[string]any{"isActive": true}, "users", linedb.LineDbAdapterOptions{})
	if err != nil {
		log.Printf("Failed to filter active users: %v", err)
		return
	}
	endTime = time.Now()

	filterDuration := endTime.Sub(startTime)
	fmt.Printf("   Активных пользователей: %d\n", len(activeUsers))
	fmt.Printf("   Время фильтрации: %v\n", filterDuration)
}

func testHTTPServer() {
	// Настраиваем HTTP сервер
	mux := http.NewServeMux()

	// Обработчики API
	mux.HandleFunc("/api/users", handleUsers)
	mux.HandleFunc("/api/products", handleProducts)
	mux.HandleFunc("/api/orders", handleOrders)

	server = &http.Server{
		Addr:    ":3001",
		Handler: mux,
	}

	fmt.Printf("   Запуск HTTP сервера на порту 3001...\n")
	fmt.Printf("   Доступные эндпоинты:\n")
	fmt.Printf("   - GET /api/users - получить всех пользователей\n")
	fmt.Printf("   - POST /api/users - создать пользователя\n")
	fmt.Printf("   - GET /api/products - получить все продукты\n")
	fmt.Printf("   - POST /api/products - создать продукт\n")
	fmt.Printf("   - GET /api/orders - получить все заказы\n")
	fmt.Printf("   - POST /api/orders - создать заказ\n")

	// Запускаем сервер в горутине
	go func() {
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Printf("HTTP server error: %v", err)
		}
	}()

	// Даем серверу время на запуск
	time.Sleep(1 * time.Second)

	// Тестируем API
	fmt.Printf("   Тестирование API...\n")
	testAPIEndpoints()

	// Останавливаем сервер
	if err := server.Close(); err != nil {
		log.Printf("Failed to close server: %v", err)
	}
}

func handleUsers(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	switch r.Method {
	case "GET":
		users, err := Database.Read("users", linedb.LineDbAdapterOptions{})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		json.NewEncoder(w).Encode(APIResponse{Success: true, Data: users})

	case "POST":
		var user map[string]any
		if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		if err := Database.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		json.NewEncoder(w).Encode(APIResponse{Success: true, Message: "User created successfully"})

	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}

func handleProducts(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	switch r.Method {
	case "GET":
		products, err := Database.Read("products", linedb.LineDbAdapterOptions{})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		json.NewEncoder(w).Encode(APIResponse{Success: true, Data: products})

	case "POST":
		var product map[string]any
		if err := json.NewDecoder(r.Body).Decode(&product); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		if err := Database.Insert(product, "products", linedb.LineDbAdapterOptions{}); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		json.NewEncoder(w).Encode(APIResponse{Success: true, Message: "Product created successfully"})

	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}

func handleOrders(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	switch r.Method {
	case "GET":
		orders, err := Database.Read("orders", linedb.LineDbAdapterOptions{})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		json.NewEncoder(w).Encode(APIResponse{Success: true, Data: orders})

	case "POST":
		var order map[string]any
		if err := json.NewDecoder(r.Body).Decode(&order); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		if err := Database.Insert(order, "orders", linedb.LineDbAdapterOptions{}); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		json.NewEncoder(w).Encode(APIResponse{Success: true, Message: "Order created successfully"})

	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}

func testAPIEndpoints() {
	// Тестируем GET /api/users
	resp, err := http.Get("http://localhost:3001/api/users")
	if err != nil {
		log.Printf("Failed to test GET /api/users: %v", err)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusOK {
		fmt.Printf("   ✓ GET /api/users работает\n")
	} else {
		fmt.Printf("   ✗ GET /api/users вернул статус %d\n", resp.StatusCode)
	}

	// Тестируем POST /api/users
	userData := map[string]any{
		"id":        999,
		"username":  "apitest",
		"email":     "apitest@example.com",
		"isActive":  true,
		"role":      "user",
		"createdAt": time.Now().Unix(),
	}

	jsonData, _ := json.Marshal(userData)
	resp, err = http.Post("http://localhost:3001/api/users", "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		log.Printf("Failed to test POST /api/users: %v", err)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusOK {
		fmt.Printf("   ✓ POST /api/users работает\n")
	} else {
		fmt.Printf("   ✗ POST /api/users вернул статус %d\n", resp.StatusCode)
	}
}

func main() {
	// Очищаем тестовую папку
	os.RemoveAll("./data/test-linedb-integration")

	// Создаем опции инициализации
	initOptions := &linedb.LineDbInitOptions{
		CacheSize: 1000,
		CacheTTL:  10 * time.Second,
		DBFolder:  "./data/test-linedb-integration",
		Collections: []linedb.JSONLFileOptions{
			{
				CollectionName: "users",
				AllocSize:      256,
				IndexedFields:  []string{"id", "username", "email"},
			},
			{
				CollectionName: "products",
				AllocSize:      256,
				IndexedFields:  []string{"id", "name", "category"},
			},
			{
				CollectionName: "orders",
				AllocSize:      256,
				IndexedFields:  []string{"id", "userId", "status"},
			},
			{
				CollectionName: "orderItems",
				AllocSize:      256,
				IndexedFields:  []string{"id", "orderId", "productId"},
			},
		},
		Partitions: []linedb.PartitionCollection{
			{
				CollectionName: "orders",
				PartIDFnStr:    "userId",
			},
		},
	}

	// Создаем базу данных
	Database = linedb.NewLineDb(nil)

	// Инициализируем базу данных
	if err := Database.Init(true, initOptions); err != nil {
		log.Fatalf("Failed to init database: %v", err)
	}
	defer Database.Close()

	fmt.Println("=== LineDb Integration Tests Demo ===")

	numbers := parseTestNumbers()
	if len(numbers) > 0 {
		runSelectedTests(numbers)
	} else {
		runAllTests()
	}

	// // Тест 1: Базовые CRUD операции
	// fmt.Println("1. Базовые CRUD операции")
	// testBasicCRUDOperations()

	// // Тест 2: Сложные запросы и фильтрация
	// fmt.Println("\n2. Сложные запросы и фильтрация")
	// testComplexQueries()

	// // Тест 3: Работа с несколькими коллекциями
	// fmt.Println("\n3. Работа с несколькими коллекциями")
	// testMultipleCollections()

	// // Тест 4: Производительность
	// fmt.Println("\n4. Тест производительности")
	// testPerformance()

	// // Тест 5: HTTP API сервер
	// fmt.Println("\n5. HTTP API сервер")
	// testHTTPServer()

	fmt.Println("\n=== Все тесты завершены ===")
}
