"""
Экспериментальный запуск для всех лабиринтов и алгоритмов
Создание CSV и графиков
"""

import os
import csv
from datetime import datetime
from maze_model import Maze
from maze_builder import TextFileMazeBuilder
from pathfinding_strategies import BFSStrategy, DFSStrategy, AStarStrategy
from maze_solver import MazeSolver, SearchStats


class ExperimentRunner:
    def __init__(self):
        self.all_results = []
        self.labirints = {
            'labirint1.txt': 'Маленький (10x10) с простым путём',
            'labirint2.txt': 'Средний (50x50) с тупиками',
            'labirint3.txt': 'Большой (100x100) запутанный',
            'labirint4.txt': 'Пустой (20x20) без стен',
            'labirint5.txt': 'Без выхода (20x20)'
        }
        self.strategies = [
            BFSStrategy(),
            DFSStrategy(),
            AStarStrategy()
        ]
    
    def run_all_experiments(self):
        """Запускает эксперименты для всех лабиринтов и алгоритмов"""
        print("\n" + "="*70)
        print("ЗАПУСК ЭКСПЕРИМЕНТОВ")
        print("="*70)
        
        builder = TextFileMazeBuilder()
        
        for filename, description in self.labirints.items():
            if not os.path.exists(filename):
                print(f"\n⚠️ Файл {filename} не найден, пропускаем...")
                continue
            
            print(f"\n📁 Лабиринт: {description}")
            print(f"   Файл: {filename}")
            print("-" * 50)
            
            try:
                maze = builder.build_from_file(filename)
                maze_name = filename.replace('.txt', '')
                
                for strategy in self.strategies:
                    print(f"   Тестирование {strategy.get_name()}...", end=" ", flush=True)
                    
                    solver = MazeSolver(maze, maze_name, strategy)
                    path, stats = solver.solve_with_stats()
                    
                    self.all_results.append({
                        'лабиринт': description,
                        'стратегия': stats.algorithm_name,
                        'время_мс': stats.execution_time_ms,
                        'посещено_клеток': stats.visited_cells,
                        'длина_пути': stats.path_length,
                        'путь_найден': 'Да' if stats.path_found else 'Нет',
                        'размер': stats.maze_size
                    })
                    
                    print(f"готово! время={stats.execution_time_ms:.3f}мс, путь={stats.path_length}")
                    
            except Exception as e:
                print(f"   ❌ Ошибка: {e}")
        
        print("\n" + "="*70)
        print("ЭКСПЕРИМЕНТЫ ЗАВЕРШЕНЫ")
        print("="*70)
    
    def save_to_csv(self, filename="experiment_results.csv"):
        """Сохраняет результаты в CSV"""
        if not self.all_results:
            print("Нет результатов для сохранения!")
            return
        
        with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
            fieldnames = ['лабиринт', 'стратегия', 'время_мс', 'посещено_клеток', 'длина_пути', 'путь_найден', 'размер']
            writer = csv.DictWriter(f, fieldnames=fieldnames)
            writer.writeheader()
            writer.writerows(self.all_results)
        
        print(f"\n✅ Результаты сохранены в {filename}")
        
        # Показываем содержимое CSV
        print("\n" + "="*70)
        print("СОДЕРЖИМОЕ CSV ФАЙЛА:")
        print("="*70)
        with open(filename, 'r', encoding='utf-8-sig') as f:
            print(f.read())
    
    def create_charts(self):
        """Создаёт графики для каждого лабиринта"""
        try:
            import matplotlib.pyplot as plt
            import numpy as np
            
            print("\n" + "="*70)
            print("ПОСТРОЕНИЕ ГРАФИКОВ")
            print("="*70)
            
            # Группируем результаты по лабиринтам
            results_by_maze = {}
            for result in self.all_results:
                maze = result['лабиринт']
                if maze not in results_by_maze:
                    results_by_maze[maze] = []
                results_by_maze[maze].append(result)
            
            # Для каждого лабиринта создаём отдельный график
            for maze_name, maze_results in results_by_maze.items():
                fig, axes = plt.subplots(1, 3, figsize=(15, 5))
                fig.suptitle(f'Сравнение алгоритмов: {maze_name}', fontsize=14, fontweight='bold')
                
                algorithms = [r['стратегия'] for r in maze_results]
                
                # График 1: Время выполнения
                times = [r['время_мс'] for r in maze_results]
                bars1 = axes[0].bar(algorithms, times, color=['blue', 'green', 'red'])
                axes[0].set_ylabel('Время (мс)')
                axes[0].set_title('Время выполнения')
                axes[0].tick_params(axis='x', rotation=15)
                for bar, val in zip(bars1, times):
                    axes[0].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
                               f'{val:.3f}', ha='center', va='bottom', fontsize=9)
                
                # График 2: Посещённые клетки
                visited = [r['посещено_клеток'] for r in maze_results]
                bars2 = axes[1].bar(algorithms, visited, color=['blue', 'green', 'red'])
                axes[1].set_ylabel('Количество клеток')
                axes[1].set_title('Посещённые клетки')
                axes[1].tick_params(axis='x', rotation=15)
                for bar, val in zip(bars2, visited):
                    axes[1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
                               f'{val:.0f}', ha='center', va='bottom', fontsize=9)
                
                # График 3: Длина пути
                lengths = [r['длина_пути'] for r in maze_results]
                bars3 = axes[2].bar(algorithms, lengths, color=['blue', 'green', 'red'])
                axes[2].set_ylabel('Шагов')
                axes[2].set_title('Длина найденного пути')
                axes[2].tick_params(axis='x', rotation=15)
                for bar, val in zip(bars3, lengths):
                    axes[2].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
                               f'{val:.0f}', ha='center', va='bottom', fontsize=9)
                
                plt.tight_layout()
                
                # Сохраняем график
                safe_name = maze_name.replace(' ', '_').replace('(', '').replace(')', '').replace('×', 'x')
                filename = f"chart_{safe_name}.png"
                plt.savefig(filename, dpi=150, bbox_inches='tight')
                print(f"✅ Сохранён: {filename}")
                plt.close()
            
            # Общий сводный график
            self._create_summary_chart()
            
        except ImportError:
            print("\n⚠️ Для построения графиков установите matplotlib:")
            print("   pip install matplotlib numpy")
    
    def _create_summary_chart(self):
        """Создаёт сводный график по всем лабиринтам"""
        import matplotlib.pyplot as plt
        import numpy as np
        
        fig, axes = plt.subplots(2, 2, figsize=(14, 10))
        fig.suptitle('Сводное сравнение алгоритмов по всем лабиринтам', fontsize=14, fontweight='bold')
        
        # Получаем уникальные лабиринты и алгоритмы
        mazes = list(set([r['лабиринт'] for r in self.all_results]))
        algorithms = ['BFS (Поиск в ширину)', 'DFS (Поиск в глубину)', 'A* (A-Star)']
        
        # 1. Время по лабиринтам
        ax1 = axes[0, 0]
        x = np.arange(len(mazes))
        width = 0.25
        
        for i, algo in enumerate(algorithms):
            times = []
            for maze in mazes:
                result = next((r for r in self.all_results if r['лабиринт'] == maze and r['стратегия'] == algo), None)
                times.append(result['время_мс'] if result else 0)
            bars = ax1.bar(x + (i - 1) * width, times, width, label=algo)
            for bar, val in zip(bars, times):
                if val > 0:
                    ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
                           f'{val:.1f}', ha='center', va='bottom', fontsize=7)
        
        ax1.set_xlabel('Лабиринт')
        ax1.set_ylabel('Время (мс)')
        ax1.set_title('Сравнение времени выполнения')
        ax1.set_xticks(x)
        ax1.set_xticklabels([m[:20] for m in mazes], rotation=45, ha='right')
        ax1.legend()
        
        # 2. Посещённые клетки
        ax2 = axes[0, 1]
        for i, algo in enumerate(algorithms):
            visited = []
            for maze in mazes:
                result = next((r for r in self.all_results if r['лабиринт'] == maze and r['стратегия'] == algo), None)
                visited.append(result['посещено_клеток'] if result else 0)
            bars = ax2.bar(x + (i - 1) * width, visited, width, label=algo)
            for bar, val in zip(bars, visited):
                if val > 0:
                    ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 5,
                           f'{val:.0f}', ha='center', va='bottom', fontsize=7)
        
        ax2.set_xlabel('Лабиринт')
        ax2.set_ylabel('Посещённые клетки')
        ax2.set_title('Сравнение посещённых клеток')
        ax2.set_xticks(x)
        ax2.set_xticklabels([m[:20] for m in mazes], rotation=45, ha='right')
        ax2.legend()
        
        # 3. Длина пути
        ax3 = axes[1, 0]
        for i, algo in enumerate(algorithms):
            lengths = []
            for maze in mazes:
                result = next((r for r in self.all_results if r['лабиринт'] == maze and r['стратегия'] == algo), None)
                lengths.append(result['длина_пути'] if result else 0)
            bars = ax3.bar(x + (i - 1) * width, lengths, width, label=algo)
            for bar, val in zip(bars, lengths):
                if val > 0:
                    ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
                           f'{val:.0f}', ha='center', va='bottom', fontsize=7)
        
        ax3.set_xlabel('Лабиринт')
        ax3.set_ylabel('Длина пути (шагов)')
        ax3.set_title('Сравнение длины пути')
        ax3.set_xticks(x)
        ax3.set_xticklabels([m[:20] for m in mazes], rotation=45, ha='right')
        ax3.legend()
        
        # 4. Таблица результатов
        ax4 = axes[1, 1]
        ax4.axis('tight')
        ax4.axis('off')
        
        # Создаём таблицу
        table_data = []
        for maze in mazes:
            row = [maze[:25]]
            for algo in algorithms:
                result = next((r for r in self.all_results if r['лабиринт'] == maze and r['стратегия'] == algo), None)
                if result:
                    row.append(f"{result['время_мс']:.1f}мс")
                else:
                    row.append("-")
            table_data.append(row)
        
        columns = ['Лабиринт', 'BFS', 'DFS', 'A*']
        table = ax4.table(cellText=table_data, colLabels=columns, cellLoc='center', loc='center')
        table.auto_set_font_size(False)
        table.set_fontsize(9)
        table.scale(1.2, 1.5)
        ax4.set_title('Сводная таблица (время в мс)', fontsize=10)
        
        plt.tight_layout()
        plt.savefig('summary_chart.png', dpi=150, bbox_inches='tight')
        print("Сохранён: summary_chart.png")
        plt.close()


def main():
    
    runner = ExperimentRunner()
    runner.run_all_experiments()
    runner.save_to_csv("experiment_results.csv")
    runner.create_charts()
    

if __name__ == "__main__":
    main()