"""A declared nonseparable fixed-scale predictive calibration, not gyro-interp."""
from pathlib import Path
import numpy as np
from age_calibration_prototype import fit_reference
from inspect_cluster_samples import read_benchmarks


class EmpiricalCalibration:
    def __init__(self, table=Path('inputs/curtis2020-source/Benchmark_Clusters.txt'), excluded_cluster=None):
        stars = read_benchmarks(table)
        self.anchors = []
        # NGC6819 is an entire independent holdout: including it at low temperature
        # would require extrapolating beyond its actual warm-star coverage.
        for name, period_width in [('Praesepe', 1.), ('NGC 6811', 1.), ('Ruprecht 147', 2.)]:
            if name == excluded_cluster:
                continue
            selected = [row for row in stars if row['cluster'] == name and 4200 <= row['teff_k'] <= 6000]
            coefficients = fit_reference(selected, degree=3)
            coordinate = (np.array([row['teff_k'] for row in selected]) - 5100) / 900
            residual = np.log([row['period_days'] for row in selected]) - np.polynomial.polynomial.polyval(coordinate, coefficients)
            scatter = max(.01, 1.4826 * np.median(abs(residual - np.median(residual))))
            self.anchors.append({'cluster': name, 'age_gyr': selected[0]['age_gyr'], 'stars': len(selected), 'coefficients': coefficients.tolist(),
                                 'temperature_range_k': [min(row['teff_k'] for row in selected), max(row['teff_k'] for row in selected)],
                                 'effective_log_scatter': float(scatter), 'minimum_predictive_period_width_days': period_width})
        self.anchors.sort(key=lambda row: row['age_gyr'])
        self.temperature_bounds = [max(row['temperature_range_k'][0] for row in self.anchors), min(row['temperature_range_k'][1] for row in self.anchors)]
        self.age_bounds = [self.anchors[0]['age_gyr'], self.anchors[-1]['age_gyr']]
        assert len(self.anchors) >= 2 and self.temperature_bounds[0] < self.temperature_bounds[1]

    def sequence(self, temperatures, ages):
        temperatures = np.atleast_1d(temperatures)
        ages = np.atleast_1d(ages)
        assert np.all((temperatures >= self.temperature_bounds[0]) & (temperatures <= self.temperature_bounds[1]))
        assert np.all((ages >= self.age_bounds[0]) & (ages <= self.age_bounds[1]))
        coordinate = (temperatures - 5100) / 900
        means = []
        widths = []
        for anchor in self.anchors:
            mean = np.polynomial.polynomial.polyval(coordinate, anchor['coefficients'])
            fractional = anchor['minimum_predictive_period_width_days'] / np.exp(mean)
            width = np.maximum(anchor['effective_log_scatter'], np.sqrt(np.log1p(fractional ** 2)))
            means.append(mean); widths.append(width)
        log_anchors = np.log([row['age_gyr'] for row in self.anchors])
        mean = np.array([np.interp(np.log(ages), log_anchors, np.array(means)[:, index]) for index in range(len(temperatures))])
        width = np.array([np.interp(np.log(ages), log_anchors, np.array(widths)[:, index]) for index in range(len(temperatures))])
        return mean, width

    def aggregate(self):
        return {'kind': 'Own fitted aggregate predictive calibration; no original or transformed star rows',
                'version': 'ac27-fixed-scale-three-anchor-v1', 'anchors': self.anchors, 'common_temperature_support_k': self.temperature_bounds,
                'age_support_gyr': self.age_bounds, 'temperature_coordinate': '(Teff-5100)/900',
                'interpolation': 'Linear log period and effective log width in log age; no forced monotonicity or hidden extrapolation',
                'width_qualification': 'Effective predictive width combines residual MAD and source-inspired one/two-day dispersion; not a measured intrinsic scatter or homogeneous period-error estimate',
                'holdouts': ['NGC 6819', 'NGC 752'], 'scale': 'Fixed published0.67/1.0/2.7Gyr conditioning; NGC6819 stays a2.5Gyr external cluster holdout, not an invented low-temperature anchor',
                'limits': 'Shared source temperature/extinction and membership assumptions retained; no external age-scale propagation or absolute precision claim'}
