"""
Metrics Visualizer.
Reads a CSV generated by the analyzer and generates a professional time-series plot.

Supports dynamic subplots for Levels (dB) and Spectral Flux.
It automatically detects if absolute timestamps (ISO 8601) are present in the CSV
and formats the X-axis as "Time of Day" (HH:MM:SS) accordingly.

Author: Daniel Collier
GitHub: https://github.com/danielfcollier
Year: 2025
"""

import argparse
import logging
import sys
from pathlib import Path

import pandas as pd

# Configure logging
logging.basicConfig(level=logging.INFO, format="%(message)s", stream=sys.stdout)
logger = logging.getLogger(__name__)


class MetricsVisualizer:
    """
    Handles loading CSV data and rendering matplotlib charts.
    """

    def __init__(self, csv_path: str):
        self.csv_path = Path(csv_path).resolve()
        if not self.csv_path.exists():
            raise FileNotFoundError(f"CSV file not found: {self.csv_path}")

        logger.info(f"Loading data from {self.csv_path}...")
        self.df = pd.read_csv(self.csv_path)

    def plot(self, requested_metrics: list[str], save_path: str | bool | None = None):
        """
        Generates the plot based on selected metrics.
        """
        # Backend setup must happen before pyplot import
        import matplotlib

        if save_path:
            matplotlib.use("Agg")

        import matplotlib.dates as mdates
        import matplotlib.pyplot as plt

        # 1. Filter Available Metrics
        available_cols = set(self.df.columns)

        # Identify valid level metrics
        level_metrics = []
        for m in ["dbfs", "lufs", "dbspl"]:
            if m in [rm.lower() for rm in requested_metrics] and m in available_cols:
                # Specific check for dBSPL data validity
                if m == "dbspl" and not pd.to_numeric(self.df["dbspl"], errors="coerce").notna().any():
                    logger.warning("dBSPL column is empty/invalid. Skipping.")
                    continue
                level_metrics.append(m)

        show_flux = "flux" in [rm.lower() for rm in requested_metrics] and "flux" in available_cols

        if not level_metrics and not show_flux:
            logger.error("No valid metrics found to plot.")
            return

        # 2. Setup Figure
        nrows = 2 if (level_metrics and show_flux) else 1
        fig, axes = plt.subplots(nrows=nrows, ncols=1, figsize=(14, 4 * nrows + 2), sharex=True)

        # Normalize axes to variables
        if nrows == 2:
            ax_levels, ax_flux = axes
        else:
            ax_levels = axes if level_metrics else None
            ax_flux = axes if show_flux else None

        fig.suptitle(f"Audio Analysis: {self.csv_path.name}", fontsize=16)

        # 3. Determine X-Axis (Time)
        x_data, is_absolute = self._get_time_axis()

        # 4. Plot Levels
        if ax_levels:
            styles = {
                "dbfs": {"label": "dBFS", "color": "#3498db", "alpha": 0.7, "style": "-"},
                "lufs": {"label": "LUFS", "color": "#2c3e50", "alpha": 1.0, "style": "-"},
                "dbspl": {"label": "dBSPL", "color": "#e74c3c", "alpha": 1.0, "style": "--"},
            }

            for metric in level_metrics:
                s = styles.get(metric, {})
                ax_levels.plot(
                    x_data,
                    self.df[metric],
                    label=s.get("label", metric),
                    color=s.get("color"),
                    linestyle=s.get("style"),
                    alpha=s.get("alpha"),
                )

            ax_levels.set_ylabel("Level (dB)")
            ax_levels.grid(True, which="both", linestyle="--", alpha=0.5)
            ax_levels.legend(loc="upper right")

        # 5. Plot Flux
        if ax_flux:
            ax_flux.plot(x_data, self.df["flux"], label="Flux", color="#e67e22")
            ax_flux.fill_between(x_data, self.df["flux"], color="#e67e22", alpha=0.3)
            ax_flux.set_ylabel("Flux Index")
            ax_flux.grid(True, linestyle="--", alpha=0.5)
            ax_flux.legend(loc="upper right")

        # 6. Format X-Axis
        target_ax = ax_flux if ax_flux else ax_levels
        if is_absolute:
            target_ax.set_xlabel("Time of Day")
            target_ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S"))
            fig.autofmt_xdate()
        else:
            target_ax.set_xlabel("Time (seconds)")

        plt.tight_layout()

        # 7. Output
        if save_path:
            # Handle boolean flag case (const=True from argparse)
            output_file = self.csv_path.with_suffix(".png") if save_path is True else Path(save_path)
            plt.savefig(output_file)
            logger.info(f"✅ Plot saved to: {output_file}")
        else:
            logger.info("🖥️  Displaying plot...")
            plt.show()

    def _get_time_axis(self):
        """Returns (x_data_series, is_absolute_bool)."""
        if "timestamp" in self.df.columns:
            try:
                # Attempt to parse absolute timestamps
                return pd.to_datetime(self.df["timestamp"]), True
            except Exception:
                pass
        return self.df["time_sec"], False


def main():
    parser = argparse.ArgumentParser(description="Visualize audio metrics from CSV.")
    parser.add_argument("csv_file", help="Path to analysis CSV")
    parser.add_argument("--save", nargs="?", const=True, default=None, help="Save to image instead of showing")
    parser.add_argument("--metrics", nargs="+", default=["dbfs", "lufs", "dbspl", "flux"], help="Metrics to include")

    args = parser.parse_args()

    try:
        viz = MetricsVisualizer(args.csv_file)
        viz.plot(args.metrics, args.save)
    except Exception as e:
        logger.exception(f"Plotting failed: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()
