import time
import random
import csv
import sys
from linked_list_phonebook import *
from hash_table_phonebook import *
from bst_phonebook import *

sys.setrecursionlimit(100000)

def generate_test_data(n=10000):
    """Генерация тестовых данных"""
    uniform_records = [(f"User_{i:05d}", f"+7-999-{i:07d}") for i in range(n)]
    
    shuffled_records = uniform_records.copy()
    random.shuffle(shuffled_records)
    
    sorted_records = sorted(uniform_records, key=lambda x: x[0])
    
    existing_names = [f"User_{i:05d}" for i in random.sample(range(n), 100)]
    non_existing_names = [f"None_{i:05d}" for i in range(10)]
    search_names = existing_names + non_existing_names
    
    delete_names = [f"User_{i:05d}" for i in random.sample(range(n), 50)]
    
    return {
        'shuffled': shuffled_records,
        'sorted': sorted_records,
        'search_names': search_names,
        'delete_names': delete_names
    }

def run_benchmarks():
    print("Генерация тестовых данных...")
    N = 10000
    test_data = generate_test_data(N)
    
    results = []
    
    structures = [
        ('LinkedList', 'll'),
        ('HashTable', 'ht'),
        ('BST', 'bst')
    ]
    
    modes = [
        ('случайный', test_data['shuffled']),
        ('отсортированный', test_data['sorted'])
    ]
    
    REPEATS = 5  # Количество повторов
    
    for struct_name, struct_type in structures:
        print(f"\n=== Тестирование {struct_name} ===")
        
        for mode_name, records in modes:
            print(f"  Режим: {mode_name}")
            
            # Запускаем 5 повторов для каждой комбинации
            for rep in range(1, REPEATS + 1):
                print(f"    Повтор {rep}/{REPEATS}...")
                
                # Создаем структуру и меряем вставку
                if struct_type == 'll':
                    structure = None
                    start = time.perf_counter()
                    for name, phone in records:
                        structure = ll_insert(structure, name, phone)
                    end = time.perf_counter()
                    insert_time = end - start
                    
                elif struct_type == 'ht':
                    structure = create_hash_table(5000)
                    start = time.perf_counter()
                    for name, phone in records:
                        ht_insert(structure, name, phone)
                    end = time.perf_counter()
                    insert_time = end - start
                    
                elif struct_type == 'bst':
                    structure = None
                    start = time.perf_counter()
                    for name, phone in records:
                        structure = bst_insert(structure, name, phone)
                    end = time.perf_counter()
                    insert_time = end - start
                
                results.append([struct_name, mode_name, "вставка", insert_time, rep])
                
                # Поиск
                start = time.perf_counter()
                for name in test_data['search_names']:
                    if struct_type == 'll':
                        ll_find(structure, name)
                    elif struct_type == 'ht':
                        ht_find(structure, name)
                    elif struct_type == 'bst':
                        bst_find(structure, name)
                end = time.perf_counter()
                find_time = end - start
                results.append([struct_name, mode_name, "поиск", find_time, rep])
                
                # Удаление
                start = time.perf_counter()
                for name in test_data['delete_names']:
                    if struct_type == 'll':
                        structure = ll_delete(structure, name)
                    elif struct_type == 'ht':
                        ht_delete(structure, name)
                    elif struct_type == 'bst':
                        structure = bst_delete(structure, name)
                end = time.perf_counter()
                delete_time = end - start
                results.append([struct_name, mode_name, "удаление", delete_time, rep])
    
    # Сохраняем в CSV с колонкой "Повтор"
    with open('docs/data/results.csv', 'w', newline='', encoding='utf-8') as f:
        writer = csv.writer(f)
        writer.writerow(['Структура', 'Режим', 'Операция', 'Время (сек)', 'Повтор'])
        writer.writerows(results)
    
    # Подсчет средних значений
    print("\n" + "="*70)
    print("СРЕДНИЕ ЗНАЧЕНИЯ (по 5 повторам)")
    print("="*70)
    
    # Собираем данные для средних
    from collections import defaultdict
    avg_data = defaultdict(list)
    for row in results:
        key = (row[0], row[1], row[2])
        avg_data[key].append(row[3])
    
    print(f"{'Структура':15} {'Режим':13} {'Операция':10} {'Среднее время':>12}")
    print("-"*55)
    for (struct, mode, op), times in avg_data.items():
        avg_time = sum(times) / len(times)
        print(f"{struct:15} {mode:13} {op:10} {avg_time:12.6f}")
    
    print(f"\nВсе замеры (5 повторов) сохранены в docs/data/results.csv")

if __name__ == "__main__":
    random.seed(42)
    run_benchmarks()