import csv
import os
import sys

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "codes"))

from maze import TextFileMazeBuilder, MazeSolver, BFSStrategy, DFSStrategy, AStarStrategy
from maze_generator import generate_all

try:
    import matplotlib.pyplot as plt
    import matplotlib
    matplotlib.use("Agg")
    HAS_PLT = True
except ImportError:
    HAS_PLT = False

BASE_DIR    = os.path.dirname(__file__)
MAZES_DIR   = os.path.join(BASE_DIR, "mazes")
RESULTS_DIR = os.path.join(BASE_DIR, "results")
RUNS = 7

MAZE_FILES = [
    ("small",   "small.txt"),
    ("medium",  "medium.txt"),
    ("large",   "large.txt"),
    ("empty",   "empty.txt"),
    ("no_exit", "no_exit.txt"),
]


def run():
    os.makedirs(RESULTS_DIR, exist_ok=True)

    if not os.path.exists(MAZES_DIR) or not os.listdir(MAZES_DIR):
        generate_all(MAZES_DIR)

    strategies = [BFSStrategy(), DFSStrategy(), AStarStrategy()]
    builder = TextFileMazeBuilder()
    all_results = []

    for label, filename in MAZE_FILES:
        path = os.path.join(MAZES_DIR, filename)
        if not os.path.exists(path):
            continue

        maze = builder.build_from_file(path)
        print(f"\nMaze: {label} ({maze.width}x{maze.height})")

        solver = MazeSolver(maze, strategies[0])

        for strategy in strategies:
            solver.set_strategy(strategy)
            times, visited_list, lengths = [], [], []

            for _ in range(RUNS):
                stats = solver.solve()
                times.append(stats.time_ms)
                visited_list.append(stats.visited)
                lengths.append(stats.path_length)

            avg_time    = sum(times) / RUNS
            avg_visited = sum(visited_list) / RUNS
            avg_len     = sum(lengths) / RUNS

            found = f"length={avg_len:.0f}" if avg_len > 0 else "not found"
            print(f"  {strategy.name:<6}  time={avg_time:.4f} ms  visited={avg_visited:.0f}  {found}")

            all_results.append({
                "maze":         label,
                "strategy":     strategy.name,
                "time_ms":      round(avg_time, 4),
                "visited_cells": round(avg_visited, 1),
                "path_length":  round(avg_len, 1),
            })

    save_csv(all_results)
    save_plots(all_results)
    show_sample()
    print("\nDone. See results/ and docs/")


def save_csv(results):
    path = os.path.join(RESULTS_DIR, "results_maze.csv")
    with open(path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(
            f, fieldnames=["maze", "strategy", "time_ms", "visited_cells", "path_length"]
        )
        writer.writeheader()
        writer.writerows(results)
    print(f"\nCSV saved: {path}")


def save_plots(results):
    if not HAS_PLT:
        return

    mazes      = list(dict.fromkeys(r["maze"] for r in results))
    strategies = list(dict.fromkeys(r["strategy"] for r in results))
    colors     = ["#2196F3", "#FF5722", "#4CAF50"]

    def val(maze, strat, key):
        for r in results:
            if r["maze"] == maze and r["strategy"] == strat:
                return float(r[key])
        return 0.0

    metrics = [
        ("time_ms",        "Time (ms)"),
        ("visited_cells",  "Visited cells"),
        ("path_length",    "Path length"),
    ]

    fig, axes = plt.subplots(
        len(metrics), len(mazes),
        figsize=(3.5 * len(mazes), 4 * len(metrics))
    )

    def fmt(v):
        if v == 0:
            return "0"
        if v >= 100:
            return f"{v:.0f}"
        if v >= 1:
            return f"{v:.2f}"
        return f"{v:.3f}"

    for row_i, (key, ylabel) in enumerate(metrics):
        for col_i, maze in enumerate(mazes):
            ax = axes[row_i][col_i]
            vals = [val(maze, s, key) for s in strategies]
            bars = ax.bar(strategies, vals, color=colors[:len(strategies)])
            if row_i == 0:
                ax.set_title(maze, fontsize=9)
            if col_i == 0:
                ax.set_ylabel(ylabel)
            for bar, v in zip(bars, vals):
                ax.text(
                    bar.get_x() + bar.get_width() / 2,
                    bar.get_height() * 1.02,
                    fmt(v), ha="center", va="bottom", fontsize=7
                )
            ax.tick_params(axis="x", labelsize=8)

    plt.tight_layout()
    out = os.path.join(RESULTS_DIR, "benchmark_plot.png")
    plt.savefig(out, dpi=120)
    plt.close()
    print(f"Chart saved: {out}")


def show_sample():
    path = os.path.join(MAZES_DIR, "sample.txt")
    if not os.path.exists(path):
        return
    builder = TextFileMazeBuilder()
    maze = builder.build_from_file(path)
    solver = MazeSolver(maze, BFSStrategy())
    stats = solver.solve()
    print("\nSample maze with BFS path:")
    print(maze.render(path=stats.path))


if __name__ == "__main__":
    run()
