"""
Configuration of trspecfit plotting functions
"""

import copy as cp
from dataclasses import dataclass

# Plot configuration hierarchy:
#
# Project (defaults from YAML)
#    ↓
# File (can customize persistently per file)
#    ↓
# Model (inherits from File, no customization)
#    ↓
# plot call (can override temporarily for one plot)


@dataclass
class PlotConfig:
    """
    Configuration for plot appearance and behavior.

    This class bundles all plot-related settings that can be passed to
    plotting functions. It can be created standalone or from a Project instance.

    Attributes
    ----------
    x_label : str
        X-axis label (energy for 2D, energy or time for 1D)
    y_label : str
        Y-axis label (time for 2D, intensity for 1D)
    z_label : str
        Z-axis/colorbar label (intensity for 2D plots)
    title : str
        Plot title
    x_dir : str
        X-axis direction: 'def' (default) or 'rev' (reversed)
    x_type : str
        X-axis scale: 'lin' (linear) or 'log' (logarithmic)
    y_dir : str
        Y-axis direction: 'def' (default) or 'rev' (reversed)
    y_type : str
        Y-axis scale: 'lin' (linear) or 'log' (logarithmic)
    x_lim : tuple[float, float] | None
        X-axis limits (min, max)
    y_lim : tuple[float, float] | None
        Y-axis limits (min, max)
    z_type : str
        Color scale type: 'lin' (linear) or 'log' (logarithmic)
    z_lim : tuple[float, float] | None
        Color scale limits for 2D plots (min, max)
    dpi_plot : int
        DPI for displaying plots
    dpi_save : int
        DPI for saving plots
    z_colormap : str
        Colormap name for 2D plots
    data_slice : list[list[int]] | None
        Data slicing indices for 2D plots: [[x_start, x_stop], [y_start, y_stop]]
    colors : list[str] | None
        List of colors for line plots
    linestyles : list[str] | None
        List of line styles
    linewidths : list[float] | None
        List of line widths
    markers : list[str] | None
        List of marker styles
    markersizes : list[float] | None
        List of marker sizes
    alphas : list[float] | None
        List of opacity values (0–1) for each trace
    legend : list[str] | None
        List of legend labels
    waterfall : float
        Y-offset between plots for waterfall display
    vlines : list[float] | None
        X-coordinates for vertical lines
    hlines : list[float] | None
        Y-coordinates for horizontal lines
    ticksize : float | None
        Font size for tick labels

    Examples
    --------
    Create a configuration with custom settings:

    >>> config = PlotConfig(x_label='Energy (eV)', x_dir='rev', dpi_plot=150)
    >>> plot_1d(data, x, config=config)

    Create from Project settings:

    >>> project = Project(path='...', config_file='project.yaml')
    >>> config = PlotConfig.from_project(project)
    >>> plot_1d(data, x, config=config)

    Override Project settings:

    >>> config = PlotConfig.from_project(project, x_label='Binding Energy (eV)')
    >>> plot_2d(data, x, y, config=config)

    Override config for a specific plot:

    >>> config = PlotConfig.from_project(project)
    >>> plot_1d(data, x, config=config, x_dir='rev', linewidth=2)

    Create multiple configs from one project:

    >>> project = Project(path='...')
    >>> default_config = PlotConfig.from_project(project)
    >>> pub_config = PlotConfig.from_project(project, dpi_save=600)
    >>> talk_config = PlotConfig.from_project(project, dpi_plot=150)
    """

    # Axis labels
    x_label: str = "x axis"
    y_label: str = "y axis"
    z_label: str = "z axis"
    title: str = ""

    # Axis behavior
    x_dir: str = "def"
    x_type: str = "lin"
    y_dir: str = "def"
    y_type: str = "lin"

    # Axis limits
    x_lim: tuple[float, float] | None = None
    y_lim: tuple[float, float] | None = None

    # Display settings
    dpi_plot: int = 100
    dpi_save: int = 300

    # Residual multiplier for 1D fit plots
    res_mult: float = 5

    # 2D plot settings
    z_colormap: str = "viridis"
    z_colorbar: str = "ver"  # 'ver' or 'hor'
    z_type: str = "lin"  # 'lin' or 'log' for color scale
    # 2D data handling
    data_slice: list[list[int]] | None = None
    z_lim: tuple[float, float] | None = None

    # Line plot styles
    colors: list[str] | None = None
    linestyles: list[str] | None = None
    linewidths: list[float] | None = None
    markers: list[str] | None = None
    markersizes: list[float] | None = None
    alphas: list[float] | None = None

    # Plot annotations
    legend: list[str] | None = None
    waterfall: float = 0
    vlines: list[float] | None = None
    hlines: list[float] | None = None
    ticksize: float | None = None

    # Normalization
    y_norm: int = 0  # 0: no normalization, 1: normalize to [0,1]
    y_scale: list[float] | None = None

    @classmethod
    def from_project(cls, project, **overrides) -> "PlotConfig":
        """
        Create PlotConfig from Project settings.

        Parameters
        ----------
        project : Project
            Project instance with plot settings
        **overrides : dict
            Any parameters to override from project defaults

        Returns
        -------
        PlotConfig
            Configuration object with settings from project
        """

        config_dict = {
            "x_label": project.e_label,
            "y_label": project.t_label,
            "z_label": project.z_label,
            "x_dir": project.x_dir,
            "x_type": project.x_type,
            "y_dir": project.y_dir,
            "y_type": project.y_type,
            "dpi_plot": project.dpi_plt,
            "dpi_save": project.dpi_save,
            "z_colormap": project.z_colormap,
            "z_colorbar": project.z_colorbar,
            "z_type": project.z_type,
            "res_mult": project.res_mult,
        }

        # Apply any overrides
        config_dict.update(overrides)

        return cls(**config_dict)

    #
    def update(self, **kwargs) -> "PlotConfig":
        """
        Update configuration attributes.

        Parameters
        ----------
        **kwargs : dict
            Attributes to update

        Returns
        -------
        PlotConfig
            Self (for chaining)
        """

        for key, value in kwargs.items():
            if hasattr(self, key):
                setattr(self, key, value)
            else:
                raise AttributeError(f"PlotConfig has no attribute '{key}'")

        return self

    #
    def copy(self, **overrides) -> "PlotConfig":
        """
        Create a copy of this config with optional overrides.

        Parameters
        ----------
        **overrides : dict
            Attributes to override in the copy

        Returns
        -------
        PlotConfig
            New configuration object
        """

        new_config = cp.copy(self)
        new_config.update(**overrides)

        return new_config
