#!/usr/bin/env python3
"""An explicit attention-window record, not an automatic task-time predictor."""
import argparse
import json
import math
from pathlib import Path
import time

STATES = ('working', 'waiting', 'needs-input', 'ready')
PHASES = ('research', 'implementation', 'verification', 'external', 'review')


def number(value):
    return type(value) in (int, float) and math.isfinite(value)


def validate(record):
    if not isinstance(record, dict) or record.get('state') not in STATES or record.get('phase') not in PHASES:
        raise ValueError('invalid state or phase')
    if not number(record.get('updated')):
        raise ValueError('invalid timestamp')
    low, high = record.get('minimum'), record.get('maximum')
    if (low is None) != (high is None):
        raise ValueError('both range endpoints are required')
    if low is not None and not (number(low) and number(high) and 0 <= low <= high <= 1440):
        raise ValueError('range must be finite, ordered and between 0 and 1440 minutes')
    return record


def render(record, now):
    validate(record)
    state, phase = record['state'], record['phase']
    age = now - record['updated']
    if age < 0:
        return 'Clock changed | refresh the record'
    if age >= 86400:
        return 'Stale record | refresh or remove it'
    if state == 'needs-input':
        return f'{phase} | INPUT NEEDED | reported {math.floor(age / 60)}m ago'
    if state == 'ready':
        return f'{phase} | READY FOR REVIEW | checks are not implied'
    label = 'WORKING' if state == 'working' else 'WAITING EXTERNALLY'
    low, high = record['minimum'], record['maximum']
    if high is None:
        return f'{phase} | {label} | next attention window unknown'
    elapsed = age / 60
    if elapsed >= high:
        return f'{phase} | {label} | declared window expired; refresh needed'
    left, right = max(0, math.ceil(low - elapsed)), math.ceil(high - elapsed)
    return f'{phase} | {label} | rough next-attention window {left}-{right}m | declared, not calibrated'


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--file', required=True, type=Path, help='Use a separate local file per task/session')
    sub = parser.add_subparsers(dest='command', required=True)
    write = sub.add_parser('write')
    write.add_argument('--state', choices=STATES, required=True)
    write.add_argument('--phase', choices=PHASES, required=True)
    write.add_argument('--min', dest='minimum', type=float)
    write.add_argument('--max', dest='maximum', type=float)
    sub.add_parser('show')
    args = parser.parse_args()
    try:
        if args.command == 'write':
            record = validate(dict(state=args.state, phase=args.phase, updated=time.time(), minimum=args.minimum, maximum=args.maximum))
            # Store only fixed state names, time and the declared range. No prompt or source content.
            # A separate file per session avoids competing writers; this is not a shared event store.
            with args.file.open('w', encoding='utf-8') as stream:
                json.dump(record, stream)
            args.file.chmod(0o600)
        else:
            record = validate(json.loads(args.file.read_text(encoding='utf-8')))
        print(render(record, time.time()))
    except (OSError, ValueError, TypeError, OverflowError):
        if args.command == 'write':
            parser.error('could not write a valid local record; check the path and range')
        print('ETA unavailable | missing or invalid local record')


if __name__ == '__main__':
    main()
