import random
import time
import csv
import os
from lab1.phonebook import *

def generate_test_data(n=10000):

    records = [(f"User_{i:05d}", f"+7-999-{i:07d}") for i in range(n)]
    
    records_shuffled = records.copy()
    random.shuffle(records_shuffled)

    records_sorted = sorted(records, key=lambda x: x[0])

    return records_shuffled, records_sorted

def get_random_names(records, n=100):
    return[name for  name, _ in random.sample(records, min(n, len(records)))]

def run_linked_experiments(records, mode_name):

    print(f"\n связный список ({mode_name}):")

    print("вставка 10000 записей:")
    
    insert_times = []
    for run in range(5):
        start = time.perf_counter()
        head = None
        for name, phone in records:
            head = ll_insert(head, name, phone)
        end = time.perf_counter()
        insert_times.append(end - start)
        print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")

    avg_insert = sum(insert_times) / 5
    print(f"среднее: {avg_insert:.6f} сек")

    print("поиск 110 записей:")

    exist_names = get_random_names(records, 100)
    non_exist_names = [f"None_{i}" for i in range(10)]

    find_times = []
    for run in range(5):
        start = time.perf_counter()

        for name in exist_names:
            ll_find(head, name)
        for name in non_exist_names:
            ll_find(head, name)

        end = time.perf_counter()
        find_times.append(end - start)
        print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")

    avg_find = sum(find_times) / 5
    print(f"среднее: {avg_find:.6f} сек")

    print("удаление 50 случайных записей:")

    to_delete = get_random_names(records,50)

    delete_times = []
    for run in range(5):
        current_head = head
        start = time.perf_counter()
        for name in to_delete:
            current_head = ll_delete(current_head, name)
        end = time.perf_counter()
        delete_times.append(end - start)
        print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")

    avg_delete = sum(delete_times) / 5
    print(f"среднее: {avg_delete:.6f} сек")

    return{
        'structure': 'LinkedList',
        'mode': mode_name,
        'insert_avg': avg_insert,
        'insert_all': insert_times,
        'find_avg': avg_find,
        'find_all': find_times,
        'delete_avg': avg_delete,
        'delete_all': delete_times
    }

def run_hash_experiments(records, mode_name):

    print(f"\n хеш-таблица({mode_name})")

    print("вставка 10000 записей:")
    
    insert_times = []
    for run in range(5):
        start = time.perf_counter()

        buckets = ht_create(1000)
        for name, phone in records:
            buckets = ht_insert(buckets, name, phone)

        end = time.perf_counter()
        insert_times.append(end - start)
        print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")

    avg_insert = sum(insert_times) / 5
    print(f"среднее: {avg_insert:.6f} сек")

    print("поиск 110 записей:")

    exist_names = get_random_names(records, 100)
    non_exist_names = [f"None_{i}" for i in range(10)]

    find_times = []
    for run in range(5):
        start = time.perf_counter()

        for name in exist_names:
            ht_find(buckets, name)
        for name in non_exist_names:
            ht_find(buckets, name)

        end = time.perf_counter()
        find_times.append(end - start)
        print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")

    avg_find = sum(find_times) / 5
    print(f"среднее: {avg_find:.6f} сек")

    print("удаление 50 случайных записей:")

    to_delete = get_random_names(records,50)

    delete_times = []
    for run in range(5):
        current_buckets = buckets.copy()
        start = time.perf_counter()
        for name in to_delete:
            current_buckets = ht_delete(current_buckets, name)
        end = time.perf_counter()
        delete_times.append(end - start)
        print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")

    avg_delete = sum(delete_times) / 5
    print(f"среднее: {avg_delete:.6f} сек")

    return{
        'structure': 'HashTable',
        'mode': mode_name,
        'insert_avg': avg_insert,
        'insert_all': insert_times,
        'find_avg': avg_find,
        'find_all': find_times,
        'delete_avg': avg_delete,
        'delete_all': delete_times
    }

def run_bst_experiments(records, mode_name):

    print(f"\n двоичное дерево({mode_name})")

    print("вставка 10000 записей:")
    
    insert_times = []
    for run in range(5):
        start = time.perf_counter()

        root = None
        for name, phone in records:
            root = bst_insert(root, name, phone)

        end = time.perf_counter()
        insert_times.append(end - start)
        print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")

    avg_insert = sum(insert_times) / 5
    print(f"среднее: {avg_insert:.6f} сек")

    print("поиск 110 записей:")

    exist_names = get_random_names(records, 100)
    non_exist_names = [f"None_{i}" for i in range(10)]

    find_times = []
    for run in range(5):
        start = time.perf_counter()

        for name in exist_names:
            bst_find(root, name)
        for name in non_exist_names:
            bst_find(root, name)

        end = time.perf_counter()
        find_times.append(end - start)
        print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")

    avg_find = sum(find_times) / 5
    print(f"среднее: {avg_find:.6f} сек")

    print("удаление 50 случайных записей:")

    to_delete = get_random_names(records,50)

    delete_times = []
    for run in range(5):
        current_root = root  
        start = time.perf_counter()
        for name in to_delete:
           current_root = bst_delete(current_root, name)
        end = time.perf_counter()
        delete_times.append(end - start)
        print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")

    avg_delete = sum(delete_times) / 5
    print(f"среднее: {avg_delete:.6f} сек")

    return{
        'structure': 'BST',
        'mode': mode_name,
        'insert_avg': avg_insert,
        'insert_all': insert_times,
        'find_avg': avg_find,
        'find_all': find_times,
        'delete_avg': avg_delete,
        'delete_all': delete_times
    }

def save_results_to_csv(all_results):
    
    os.makedirs("docs/data", exist_ok=True)
    
    with open("docs/data/results.csv", "w", encoding="utf-8") as f:
       
        f.write("Структура, Режим, Операция, Замер, Время (сек)\n")
        
        for res in all_results:
            struct = res['structure']
            mode = res['mode']
            
       
            for i, t in enumerate(res['insert_all']):
                f.write(f"{struct},{mode},вставка,{i+1},{t}\n")
            f.write(f"{struct},{mode},вставка,среднее,{res['insert_avg']}\n")
            
          
            for i, t in enumerate(res['find_all']):
                f.write(f"{struct},{mode},поиск,{i+1},{t}\n")
            f.write(f"{struct},{mode},поиск,среднее,{res['find_avg']}\n")
            
         
            for i, t in enumerate(res['delete_all']):
                f.write(f"{struct},{mode},удаление,{i+1},{t}\n")
            f.write(f"{struct},{mode},удаление,среднее,{res['delete_avg']}\n")
    

def main():
    print("эксперименты по замеру производительности")

    records_shuffled, records_sorted = generate_test_data(10000)

    all_results = []

    print("режим: случайный порядок")

    all_results.append(run_linked_experiments(records_shuffled, "случайный"))
    all_results.append(run_hash_experiments(records_shuffled, "случайный"))
    all_results.append(run_bst_experiments(records_shuffled, "случайный"))

    print("режим: отсортированный порядок")

    all_results.append(run_linked_experiments(records_sorted, "отсортированный"))
    all_results.append(run_hash_experiments(records_sorted, "отсортированный"))
    all_results.append(run_bst_experiments(records_sorted, "отсортированный"))

    save_results_to_csv(all_results)

if __name__== "__main__":
    main()



