"""Result containers for NeuroWeave model outputs."""

from __future__ import annotations

from typing import Any

import numpy as np


class EncodingResult:
    """Container for the results of an encoding model fit.

    Parameters
    ----------
    kernel : Any, optional
        Estimated model kernel or coefficient representation.
    predictions : Any, optional
        Predicted neural responses or derived outputs.
    score : Any, optional
        Model evaluation score.
    cv_results : Any, optional
        Cross-validation outputs and summaries.
    metadata : dict[str, Any] | None, optional
        Additional metadata associated with the result.
    """

    def __init__(
        self,
        kernel: Any = None,
        predictions: Any = None,
        score: Any = None,
        cv_results: Any = None,
        metadata: dict[str, Any] | None = None,
    ) -> None:
        self.kernel = kernel
        self.predictions = predictions
        self.score = score
        self.scores = score
        self.cv_results = cv_results
        self.metadata = metadata if metadata is not None else {}

    def r2(self, y_true: np.ndarray | None = None) -> float:
        """Compute the coefficient of determination.

        Parameters
        ----------
        y_true : np.ndarray | None, optional
            Ground-truth target values. If omitted, the method will use
            ``metadata["y_true"]`` when available.

        Returns
        -------
        float
            Coefficient of determination for the stored predictions.
        """

        if self.predictions is None:
            raise ValueError("Predictions are required to compute R^2.")

        if y_true is None:
            y_true = self.metadata.get("y_true")

        if y_true is None:
            raise ValueError("Ground-truth targets are required to compute R^2.")

        y_true_array = np.asarray(y_true, dtype=float).reshape(-1)
        y_pred_array = np.asarray(self.predictions, dtype=float).reshape(-1)

        numerator = np.sum((y_true_array - y_pred_array) ** 2)
        denominator = np.sum((y_true_array - np.mean(y_true_array)) ** 2)

        if denominator == 0.0:
            return float("nan")

        return float(1.0 - (numerator / denominator))

    def plot_kernel(self) -> Any:
        """Plot the estimated encoding kernel.

        Returns
        -------
        Any
            Backend-specific plotting handle.
        """

        raise NotImplementedError()

    def plot_generalisation(self) -> Any:
        """Plot temporal generalisation outputs.

        Returns
        -------
        Any
            Backend-specific plotting handle.
        """

        raise NotImplementedError()

    def plot_regularization_curve(self) -> Any:
        """Plot regularization diagnostics.

        Returns
        -------
        Any
            Backend-specific plotting handle.
        """

        raise NotImplementedError()

    def cluster_test(self) -> Any:
        """Run a cluster-based statistical test on the result.

        Returns
        -------
        Any
            Statistical test output.
        """

        raise NotImplementedError()
