import time
import csv
import os
import random

from maze_builder import TextFileMazeBuilder
from maze_solver import MazeSolver
from maze_strategies import BFSStrategy, DFSStrategy, AStarStrategy

REPEATS = 7
OUTPUT_DIR = os.path.dirname(os.path.abspath(__file__))
CSV_PATH = os.path.join(OUTPUT_DIR, 'results.csv')

STRATEGIES = {
    'BFS': BFSStrategy,
    'DFS': DFSStrategy,
    'A*':  AStarStrategy,
}

MAZES = [
    ('small_10x10',   'maze_small.txt'),
    ('medium_50x50',  'maze_medium.txt'),
    ('large_100x100', 'maze_large.txt'),
    ('open_50x50',    'maze_open.txt'),
    ('no_exit_20x20', 'maze_no_exit.txt'),
]

def _make_grid(width, height, density=0.0, has_exit=True, seed=42):

    rng = random.Random(seed)
    grid = []
    for y in range(height):
        row = []
        for x in range(width):
            on_border = (x == 0 or x == width - 1 or y == 0 or y == height - 1)
            row.append('#' if on_border else ' ')
        grid.append(row)

    for y in range(1, height - 1):
        for x in range(1, width - 1):
            if rng.random() < density:
                grid[y][x] = '#'

    grid[1][1] = 'S'
    if has_exit:
        grid[height - 2][width - 2] = 'E'

    return '\n'.join(''.join(row) for row in grid)


def generate_maze_files():
    mazes_data = {
        'maze_small.txt':   _make_grid(10,  10,  density=0.15),
        'maze_medium.txt':  _make_grid(50,  50,  density=0.28),
        'maze_large.txt':   _make_grid(100, 100, density=0.30),
        'maze_open.txt':    _make_grid(50,  50,  density=0.0),
        'maze_no_exit.txt': _make_grid(20,  20,  density=0.20, has_exit=False),
    }
    no_exit = list(mazes_data['maze_no_exit.txt'].splitlines())
    no_exit[18] = no_exit[18][:18] + 'E' + no_exit[18][19:]
    no_exit[17] = no_exit[17][:18] + '#' + no_exit[17][19:]
    no_exit[18] = no_exit[18][:17] + '#' + no_exit[18][18:]
    mazes_data['maze_no_exit.txt'] = '\n'.join(no_exit)

    maze_dir = os.path.dirname(os.path.abspath(__file__))
    for fname, content in mazes_data.items():
        path = os.path.join(maze_dir, fname)
        with open(path, 'w', encoding='utf-8') as f:
            f.write(content)

    print("✅ Файлы лабиринтов созданы")
def avg(lst):
    return sum(lst) / len(lst) if lst else 0


def run_benchmark():
    builder = TextFileMazeBuilder()
    maze_dir = os.path.dirname(os.path.abspath(__file__))

    all_results = [
        ['лабиринт', 'стратегия', 'время_мс', 'посещено_клеток', 'длина_пути']
        + [f'замер_{i+1}' for i in range(REPEATS)]
    ]

    print(f"\nЗапуск бенчмарков (повторений: {REPEATS})\n")
    print(f"  {'Лабиринт':<18} {'Алгоритм':<6} {'Время мс':>10} "
          f"{'Посещено':>10} {'Путь':>6}")
    print('  ' + '-' * 56)

    for maze_label, maze_file in MAZES:
        maze_path = os.path.join(maze_dir, maze_file)
        try:
            maze = builder.build_from_file(maze_path)
        except Exception as e:
            print(f"  ❌ {maze_file}: {e}")
            continue

        solver = MazeSolver(maze)

        for strat_name, StratClass in STRATEGIES.items():
            times_ms, visited_list, path_len = [], [], 0

            for _ in range(REPEATS):
                strat = StratClass()
                solver.set_strategy(strat)
                stats = solver.solve()
                times_ms.append(stats.time_ms)
                visited_list.append(stats.visited_cells)
                path_len = stats.path_length

            mean_t = avg(times_ms)
            mean_v = avg(visited_list)

            print(f"  {maze_label:<18} {strat_name:<6} "
                  f"{mean_t:>10.3f} {mean_v:>10.0f} {path_len:>6}")

            all_results.append([
                maze_label, strat_name,
                f"{mean_t:.4f}", f"{mean_v:.0f}", str(path_len)
            ] + [f"{t:.4f}" for t in times_ms])

    with open(CSV_PATH, 'w', newline='', encoding='utf-8') as f:
        csv.writer(f).writerows(all_results)

    print(f"\n✅ Результаты сохранены: {CSV_PATH}")

def smoke_test():
    print("=== Smoke Test ===\n")

    maze_dir = os.path.dirname(os.path.abspath(__file__))
    test_path = os.path.join(maze_dir, '_test_maze.txt')

    with open(test_path, 'w', encoding='utf-8') as f:
        f.write("#######\n#S    #\n#     #\n#    E#\n#######")

    builder = TextFileMazeBuilder()
    maze = builder.build_from_file(test_path)

    for name, StratClass in STRATEGIES.items():
        strat = StratClass()
        path = strat.find_path(maze, maze.start, maze.exit)
        assert len(path) > 0, f"{name}: путь не найден!"
        assert path[0].is_start
        assert path[-1].is_exit
        print(f"  ✅ {name}: путь длиной {len(path)} — OK")

    os.remove(test_path)
    print("\nВсе тесты пройдены!\n")

if __name__ == '__main__':
    smoke_test()
    generate_maze_files()
    run_benchmark()
