#!/usr/bin/env python3
"""Rebuild MANIFEST.tsv and SHA256SUMS.txt for the release archive."""
from __future__ import annotations

import csv
import hashlib
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
SKIP_DIRS = {'__pycache__', 'work'}
SKIP_FILES = {'MANIFEST.tsv', 'SHA256SUMS.txt'}

DESCRIPTIONS = {
    'README.md': 'Overview, directory map, and verified reproduction commands.',
    'DATA_DICTIONARY.md': 'Definitions of all data columns and file roles.',
    'CITATION.txt': 'Suggested citation for the manuscript and exact sequence data.',
    'requirements.txt': 'Python package requirements for the analysis and figures.',
    'data/counts/oeis_A048833_k0_k10000.txt': 'Compact exact A048833 data through index 10000.',
    'data/counts/oeis_A048833_k0_k10000.tsv': 'Exact A048833 data with total size, partition number, and density.',
    'data/counts/oeis_A048833_public_k0_k750.txt': 'Snapshot of the preceding public OEIS b-file.',
    'data/counts/nim_losing_counts_N0_N20000.tsv': 'Master exact data for every total size N from 0 through 20000.',
    'data/counts/exact_count_verification.txt': 'Release verification summary for the exact counts.',
    'data/counts/knapsack_check_N1500.txt': 'Output of the independent direct-knapsack check at N=1500.',
    'data/tables/table_01_first_order_milestones.tsv': 'Data printed in manuscript Table 1.',
    'data/tables/table_02_error_decomposition.tsv': 'Data printed in manuscript Table 2.',
    'data/tables/table_03_two_level_error_budget.tsv': 'Data printed in manuscript Table 3.',
    'data/tables/table_04_second_order_fixed_size.tsv': 'Data printed in manuscript Table 4.',
    'data/tables/table_05_major_arc_validation.tsv': 'Data printed in manuscript Table 5.',
    'data/figures/fig_03_04_hierarchy_and_error_sources_N2000_N20000.tsv': 'Grid data for manuscript Figures 3 and 4.',
    'data/figures/fig_05_fixed_size_correction_N2000_N20000.tsv': 'Grid data for manuscript Figure 5.',
    'data/figures/fig_06_exact_density_N100_N20000.tsv': 'Every-even-N data for manuscript Figure 6.',
    'code/exact_counts/nimxor_walsh_avx2.cpp': 'AVX2 and OpenMP modular Walsh coefficient program.',
    'code/exact_counts/run_exact_counts.sh': 'Driver for reconstruction, check-prime verification, and release comparison.',
    'code/exact_counts/reconstruct_exact_counts.py': 'CRT reconstruction and exact-range checks.',
    'code/exact_counts/verify_check_prime.py': 'Independent-prime residue checker.',
    'code/exact_counts/compare_compact_counts.py': 'Compact count-file comparison utility.',
    'code/exact_counts/verify_release_counts.py': 'Packaged-count and public-snapshot verification.',
    'code/exact_counts/moduli_example_17.txt': 'Fresh valid 17-prime example set with a 527-bit product.',
    'code/exact_counts/check_prime.txt': 'Independent check prime used in the original verification.',
    'code/analysis/common.py': 'Shared saddle, partition, and Walsh-Hadamard routines.',
    'code/analysis/build_count_data.py': 'Builds the master count table, Table 1, and Figure 6 data.',
    'code/analysis/compute_error_decomposition.py': 'Builds manuscript Table 2.',
    'code/analysis/compute_hierarchy_data.py': 'Builds manuscript Table 3 and Figures 3--4 data.',
    'code/analysis/compute_second_order_data.py': 'Builds manuscript Table 4 and Figure 5 data.',
    'code/analysis/compute_major_arc_validation.py': 'Builds manuscript Table 5.',
    'code/analysis/make_all_figures.py': 'Creates all six manuscript figures.',
    'code/analysis/run_all_figures.sh': 'Convenience wrapper for figure generation.',
    'code/analysis/rebuild_all_analysis.sh': 'Rebuilds all numerical tables, figure data, and figures.',
    'code/independent_checks/distinct_xor_knapsack.cpp': 'Direct distinct-part XOR knapsack modulo one prime.',
    'code/independent_checks/knapsack_check_N1500.py': 'Independent CRT and odd-core check at N=1500.',
    'code/independent_checks/run_all_checks.sh': 'Runs the packaged-count and direct-knapsack checks.',
    'code/independent_checks/run_quick_checks.sh': 'Runs the N=1500 exact-count and direct-knapsack checks together.',
    'code/release/rebuild_release_metadata.py': 'Rebuilds the manifest and SHA-256 checksum file.',
}


def category(path: str) -> str:
    if path.startswith('code/'):
        return 'code'
    if path.startswith('data/'):
        return 'data'
    if path.startswith('figures/'):
        return 'figure'
    return 'documentation'


def description(path: str) -> str:
    if path in DESCRIPTIONS:
        return DESCRIPTIONS[path]
    name = Path(path).name
    if path.startswith('figures/') and name.endswith('.pdf'):
        return 'Manuscript figure in vector PDF format.'
    if path.startswith('figures/') and name.endswith('.png'):
        return 'Manuscript figure in high-resolution PNG format.'
    return ''


def files() -> list[Path]:
    result = []
    for path in ROOT.rglob('*'):
        if not path.is_file():
            continue
        rel = path.relative_to(ROOT)
        if any(part in SKIP_DIRS for part in rel.parts):
            continue
        if rel.name in SKIP_FILES:
            continue
        result.append(path)
    return sorted(result, key=lambda p: p.relative_to(ROOT).as_posix())


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


def main() -> None:
    current = files()
    manifest = ROOT / 'MANIFEST.tsv'
    with manifest.open('w', newline='') as fh:
        w = csv.writer(fh, delimiter='\t', lineterminator='\n')
        w.writerow(['relative_path', 'category', 'size_bytes', 'description'])
        for path in current:
            rel = path.relative_to(ROOT).as_posix()
            w.writerow([rel, category(rel), path.stat().st_size, description(rel)])

    checksum_files = current + [manifest]
    checksum_files.sort(key=lambda p: p.relative_to(ROOT).as_posix())
    with (ROOT / 'SHA256SUMS.txt').open('w', newline='\n') as fh:
        for path in checksum_files:
            rel = path.relative_to(ROOT).as_posix()
            fh.write(f'{sha256(path)}  ./{rel}\n')

    print(manifest)
    print(ROOT / 'SHA256SUMS.txt')


if __name__ == '__main__':
    main()
