"""Verify exact selected source bytes and remeasure the actual satellite spots."""

import hashlib
import json
from pathlib import Path
import numpy as np
from image_tools import header_cards, remeasure_spot, aperture, spot_template


def load_selected(manifest_path, cache):
    manifest = json.loads(Path(manifest_path).read_text())
    (
        science,
        quality,
        headers,
        spot_measurements,
        templates,
        star_aperture,
        individual_templates,
    ) = ([], [], [], [], [], [], [])
    for entry in manifest["files"]:
        number = entry["file"].split("S20131210S")[1][:4]
        raw_header = (cache / (number + "-headers.bin")).read_bytes()
        assert hashlib.sha256(raw_header).hexdigest() == entry["header_sha256"]
        header = header_cards(raw_header)
        headers.append(header)
        (
            frame_science,
            frame_quality,
            frame_spots,
            frame_templates,
            frame_star,
            frame_individual,
        ) = ([], [], [], [], [], [])
        for channel, sci_range, dq_range in zip(
            manifest["channels"], entry["ranges"], entry["quality_ranges"]
        ):
            raw_science = (cache / ("%s-sci-%02d.bin" % (number, channel))).read_bytes()
            raw_quality = (cache / ("%s-dq-%02d.bin" % (number, channel))).read_bytes()
            assert hashlib.sha256(raw_science).hexdigest() == sci_range["sha256"]
            assert hashlib.sha256(raw_quality).hexdigest() == dq_range["sha256"]
            image = (
                np.frombuffer(raw_science, dtype=">f4").reshape(281, 281).astype(float)
            )
            flags = np.frombuffer(raw_quality, dtype="u1").reshape(281, 281)
            # Bad or either saturation bit invalidates a sample; UTR rejection and
            # repaired/interpolated bit 5 alone are retained, with a strict sensitivity run.
            image[(flags & 7) != 0] = np.nan
            measured, channel_templates, fluxes = [], [], []
            for spot in range(4):
                original = list(
                    map(float, header["SATS%d_%d" % (channel, spot)].split())
                )
                try:
                    x, y = remeasure_spot(image, *original)
                except ValueError as error:
                    raise ValueError(
                        "%s channel %d spot %d: %s" % (number, channel, spot, error)
                    ) from error
                flux = aperture(image, x, y, 3)
                measured.append(
                    {"original": original, "measured": [x, y], "aperture_adu": flux}
                )
                channel_templates.append(spot_template(image, x, y))
                fluxes.append(flux)
            template = np.mean(channel_templates, axis=0)
            # Re-normalize with the SAME aperture estimator after template averaging.
            template /= aperture(template, 6, 6, 3)
            frame_science.append(image)
            frame_quality.append(flags)
            frame_spots.append(measured)
            frame_templates.append(template)
            frame_individual.append(channel_templates)
            frame_star.append(float(np.mean(fluxes) / 10 ** (-0.4 * 9.36)))
        science.append(frame_science)
        quality.append(frame_quality)
        spot_measurements.append(frame_spots)
        templates.append(frame_templates)
        star_aperture.append(frame_star)
        individual_templates.append(frame_individual)
    return {
        "manifest": manifest,
        "science": np.asarray(science),
        "quality": np.asarray(quality),
        "headers": headers,
        "spots": spot_measurements,
        "templates": np.asarray(templates),
        "star_aperture_adu": np.asarray(star_aperture),
        "individual_templates": np.asarray(individual_templates),
    }
