"""Read the public Medyx parts using only Python's standard library."""
from __future__ import annotations
from hashlib import sha256
import json
from pathlib import Path


def checksum(path: Path) -> str:
    result = sha256()
    with path.open('rb') as stream:
        for block in iter(lambda: stream.read(1024 * 1024), b''):
            result.update(block)
    return result.hexdigest()


def contained(folder: Path, filename: str) -> Path:
    path = (folder / filename).resolve()
    if not path.is_relative_to(folder.resolve()):
        raise ValueError('Path outside dataset: ' + filename)
    return path


def load_index(folder: Path) -> tuple[dict, dict]:
    manifest = json.loads((folder / 'manifest.json').read_text(encoding='utf-8'))
    index_path = folder / 'exports.json'
    if checksum(index_path) != manifest['checksums']['exports.json']:
        raise ValueError('Checksum mismatch: exports.json')
    index = json.loads(index_path.read_text(encoding='utf-8'))
    return manifest, index


def checked_file(folder: Path, info: dict, manifest: dict) -> Path:
    path = contained(folder, info['path'])
    expected = manifest['checksums'].get(info['path'])
    if expected != info['sha256'] or checksum(path) != expected:
        raise ValueError('Checksum mismatch: ' + info['path'])
    return path


def observations(folder: Path):
    manifest, index = load_index(folder)
    seen = set()
    for part in index['parts']:
        path = checked_file(folder, part['files']['observations.jsonl'], manifest)
        with path.open(encoding='utf-8') as stream:
            for line in stream:
                if not line.strip():
                    continue
                row = json.loads(line)
                if row['id'] in seen:
                    raise ValueError('Duplicate observation: ' + row['id'])
                seen.add(row['id'])
                yield row
    if len(seen) != manifest['metrics']['change_observations']:
        raise ValueError('Observation count does not match manifest')


DEFAULT_FOLDER = Path(__file__).resolve().parents[2] / 'data/original'
