"""Reproduce constructed planning examples. No real household or outcome data.

Run: python3 everyday-decisions.py
Uses only the standard library; reads no personal files and makes no requests.
Units and exclusions are stored with each example. These are arithmetic models,
not recommendations, safety certifications or measured human performance.
"""
import json
import math


def contrast(a, b):
    def luminance(colour):
        channels = [int(colour[i:i + 2], 16) / 255 for i in (1, 3, 5)]
        linear = [c / 12.92 if c <= .04045 else ((c + .055) / 1.055) ** 2.4 for c in channels]
        return sum(c * weight for c, weight in zip(linear, (.2126, .7152, .0722)))
    high, low = sorted((luminance(a), luminance(b)), reverse=True)
    return (high + .05) / (low + .05)


def checks():
    results = {}
    results['morning-routine-that-lasts'] = {
        'unit': 'minutes after midnight', 'departure': 480, 'start': 425,
        'preparation': 35, 'buffer': 10,
        'optional_minutes': 480 - 425 - 35 - 10,
        'late_start': 437, 'late_optional_minutes': 480 - 437 - 35 - 10,
        'interpretation': '10 minutes initially; a late start leaves a 2-minute gap even without the optional task.'}
    results['setting-goals-that-work'] = {
        'unit': 'minutes', 'available': 120,
        'full_scope_tasks': [25, 70, 40, 15],
        'full_total': sum([25, 70, 40, 15]),
        'reduced_scope_tasks': [25, 35, 40, 15],
        'reduced_total': sum([25, 35, 40, 15]),
        'interpretation': '150 does not fit120; agreed scope reduction makes115. No productivity effect measured.'}
    results['pomodoro-time-management'] = {
        'unit': 'minutes', 'slot': 60, 'preparation': 7,
        'two_cycles_and_breaks': 7 + 25 + 5 + 25 + 5,
        'without_final_break': 7 + 25 + 5 + 25,
        'shorter_trial_with_both_breaks': 7 + 20 + 5 + 20 + 5,
        'interpretation': '67 and62 do not fit60;57 does. A planning choice, not an optimal biological timer.'}
    results['the-art-of-focus'] = {
        'unit': 'minutes', 'slot': 45, 'preparation': 5, 'closing': 8,
        'initial_work_budget': 45 - 5 - 8, 'necessary_interruption': 7,
        'remaining_work_budget': 45 - 5 - 8 - 7,
        'interpretation': 'Work budget falls32 to25; no universal recovery penalty is added.'}
    results['color-psychology-explained'] = {
        'unit': 'sRGB hex / contrast ratio', 'background': '#FFFFFF',
        'pairs': [{'foreground': c, 'ratio': round(contrast(c, '#FFFFFF'), 4)}
                  for c in ('#777777', '#595959', '#000000')],
        'interpretation': 'Solid text/background pairs only, not whole-page accessibility or colour-vision certification.'}
    remaining = 100001 - 10000
    payments = [remaining // 3 + (i < remaining % 3) for i in range(3)]
    results['sinking-funds-basics'] = {
        'unit': 'minor currency units,100 per unit', 'bill': 100001,
        'reserved': 10000, 'deposit_dates': ['2026-10-30', '2026-11-13', '2026-11-27'],
        'payment_date': '2026-11-30', 'remaining': remaining,
        'payments': payments, 'final_reserve': 10000 + sum(payments),
        'interpretation': '300.01,300.00,300.00 reaches1000.01 exactly. A same-day deposit is not assumed cleared.'}
    events = [('opening', 100), ('bill_before_pay', -140), ('pay_arrives', 1000), ('later_bill', -650)]
    balances = []; balance = 0
    for name, amount in events:
        balance += amount
        balances.append({'event': name, 'change': amount, 'balance': balance})
    results['first-paycheck-money-setup'] = {
        'unit': 'currency units', 'events': balances, 'lowest_balance': min(x['balance'] for x in balances),
        'end_balance': balance, 'interpretation': 'An ending310 does not remove the earlier40 shortfall. No payment or borrowing is executed.'}
    results['pantry-staples-list'] = {
        'unit': 'cans', 'physical_stock': 4, 'already_assigned_elsewhere': 2,
        'usable_for_this_plan': 4 - 2, 'planned_use': 3, 'chosen_buffer': 1,
        'buy': max(0, 3 + 1 - (4 - 2)),
        'interpretation': 'Buy2 under stated assumptions; four physical cans do not all belong to this meal plan. No food-safety inference.'}
    tops = ['A', 'B', 'C']; bottoms = ['X', 'Y']
    rejected = {'A-Y': 'does not suit the constructed occasion', 'C-X': 'awaiting repair'}
    pairs = ['-'.join((a, b)) for a in tops for b in bottoms]
    results['capsule-wardrobe-basics'] = {
        'unit': 'constructed outfit pair', 'theoretical_pairs': pairs, 'rejected': rejected,
        'available_pairs': [p for p in pairs if p not in rejected],
        'interpretation': 'Six arithmetic combinations, four in the constructed accepted set; no garments were tried by an author.'}
    results['laptop-battery-care'] = {
        'unit': 'percentage points per hour',
        'constructed_sessions': [
            {'name': 'text task', 'start_percent': 80, 'end_percent': 60, 'minutes': 60, 'rate': 20},
            {'name': 'video call', 'start_percent': 80, 'end_percent': 50, 'minutes': 60, 'rate': 30}],
        'interpretation': 'Different workloads confound a battery-health claim. Do not extrapolate a full discharge or lifespan.'}
    results['focus-music-and-noise'] = {
        'unit': 'constructed proofreading entries', 'one_known_error_per_entry': True,
        'conditions': [
            {'name': 'no added audio', 'checked': 20, 'missed': 1, 'minutes': 10, 'correct_per_minute': 19 / 10},
            {'name': 'instrumental', 'checked': 20, 'missed': 3, 'minutes': 8, 'correct_per_minute': 17 / 8}],
        'interpretation': 'The second is faster but misses more. Neither objective dominates; generated values are not an audio efficacy study.'}
    results['moving-house-checklist'] = {
        'unit': 'minutes after midnight', 'keys_confirmed_at': 600,
        'unload_requested_at': 570, 'unload_duration': 40, 'assembly_duration': 25,
        'earliest_unload_start': max(600, 570), 'earliest_unload_end': max(600, 570) + 40,
        'earliest_assembly_end': max(600, 570) + 40 + 25,
        'interpretation': 'Requested09:30 cannot precede confirmed10:00 access; earliest assembly finish11:05 assumes provider availability, not a booking.'}
    results['small-room-organization'] = {
        'unit': 'centimetres in a constructed straight cross-section', 'gap_depth': 80,
        'closed_box_depth': 50, 'drawer_extension': 40,
        'closed_fits': 50 <= 80, 'open_depth': 50 + 40,
        'open_fits': 50 + 40 <= 80,
        'interpretation': 'Closed fits; open exceeds the assumed gap by10. Does not certify circulation, anchoring or legal clearance.'}
    results['travel-packing-checklist'] = {
        'unit': 'grams', 'assumed_bag_limit': 7000, 'bag': 1600,
        'contents': [3600, 900, 650, 400], 'total': 1600 + sum([3600, 900, 650, 400]),
        'over_assumed_limit': 1600 + sum([3600, 900, 650, 400]) - 7000,
        'interpretation': '7150 exceeds a fictional7000 limit by150. Compression does not subtract mass. Not an airline or security rule.'}
    assert results['morning-routine-that-lasts']['optional_minutes'] == 10
    assert results['morning-routine-that-lasts']['late_optional_minutes'] == -2
    assert results['setting-goals-that-work']['full_total'] == 150
    assert results['setting-goals-that-work']['reduced_total'] == 115
    assert results['pomodoro-time-management']['two_cycles_and_breaks'] == 67
    assert results['pomodoro-time-management']['without_final_break'] == 62
    assert results['pomodoro-time-management']['shorter_trial_with_both_breaks'] == 57
    assert results['the-art-of-focus']['remaining_work_budget'] == 25
    assert math.isclose(contrast('#000000', '#FFFFFF'), 21)
    assert contrast('#777777', '#FFFFFF') < 4.5 < contrast('#595959', '#FFFFFF')
    assert results['sinking-funds-basics']['final_reserve'] == 100001
    assert results['first-paycheck-money-setup']['lowest_balance'] == -40
    assert results['first-paycheck-money-setup']['end_balance'] == 310
    assert results['pantry-staples-list']['buy'] == 2
    assert len(results['capsule-wardrobe-basics']['available_pairs']) == 4
    assert results['moving-house-checklist']['earliest_assembly_end'] == 665
    assert results['small-room-organization']['closed_fits'] and not results['small-room-organization']['open_fits']
    assert results['travel-packing-checklist']['over_assumed_limit'] == 150
    return results


if __name__ == '__main__':
    print(json.dumps({'provenance': 'Constructed inputs; no household, health or reader outcome observations.',
                      'cases': checks()}, ensure_ascii=False, indent=2))
