"""NeoLogic / 固定した架空例の計算・判定教材。Python 3標準機能のみ。

実行: python3 business-ai-decision-checks-20261026.py
外部接続、AI呼出し、顧客データ、実サービスの測定は行いません。
結果は例の条件だけに対応します。料金単価は説明用の固定値です。
"""
from decimal import Decimal as D
from itertools import permutations
import json


def api_cost():
    # 既存記事と同じ仮の利用量。再試行を既に含む月間値を再加算しない。
    attempts = 21000
    ordinary, cached, output = 4000, 2000, 1600
    cost = D(attempts) * (D(ordinary) * D('0.75') + D(cached) * D('0.075')
                          + D(output) * D('4.50')) / D(1000000)
    assert cost == D('217.35')
    wrong_retry = cost * D('1.05')
    wrong_reasoning = cost + D(attempts * 1000) * D('4.50') / D(1000000)
    assert wrong_retry == D('228.2175') and wrong_reasoning == D('311.85')
    return {'token_cost_usd': str(cost), 'retry_double_count_usd': str(wrong_retry),
            'reasoning_double_count_usd': str(wrong_reasoning),
            'excluded': '検索、一般クラウド、税、為替。請求実績ではない'}


def selection():
    # 本文のA/B/Cは実製品ではない。既存比較表の条件判断を独立して検算。
    rows = [('A', 197, 0, 20, 200, 2, 9, 341200),
            ('B', 190, 0, 20, 200, .4, 3, 326000),
            ('C', 196, 2, 18, 200, .3, 2, 323800)]
    def choose(rate, first, finish, budget):
        allowed = [r for r in rows if r[1] / 200 >= rate and r[2] == 0
                   and r[3] == 20 and r[4] == 200 and r[5] <= first
                   and r[6] <= finish and r[7] <= budget]
        return min(allowed, key=lambda r: r[7])[0] if allowed else None
    result = [choose(.95, 3, 10, 350000), choose(.98, 3, 10, 350000),
              choose(.95, 1, 4, 350000), choose(.98, 1, 4, 350000),
              choose(.95, 3, 10, 300000)]
    assert result == ['B', 'A', 'B', None, None]
    # 同じ200件でも全件の平均と190番目は異なる。
    times = [1] * 189 + [20] * 11
    assert sum(times) / len(times) == 2.045 and sorted(times)[189] == 20
    return {'choices': result, 'latency_counterexample': {
        'mean_seconds': sum(times) / len(times), 'p95_seconds': sorted(times)[189]}}


def schedule():
    duration = {'A': 60, 'B': 90, 'C': 30}
    valid, shifted = [], []
    for order in permutations(duration):
        t, spans = 0, {}
        for task in order:
            spans[task] = (t, t + duration[task]); t += duration[task]
        if spans['A'][1] <= spans['B'][0] and spans['C'][0] == 60 and t <= 180:
            valid.append(''.join(order))
        if spans['A'][1] <= spans['B'][0] and spans['C'][0] == 90 and t <= 180:
            shifted.append(''.join(order))
    assert valid == ['ACB'] and shifted == []
    return {'permutations_checked': 6, 'valid': valid,
            'valid_if_C_starts_at_1030': shifted,
            'time_origin': '0分=9時。分割不可・1人・待機なしで全180分を使う条件'}


def translation():
    # 自然言語翻訳器ではない。二つの明示した不等式だけを比較する。
    original = {n for n in range(0, 21) if n >= 10}
    incorrect = {n for n in range(0, 21) if n > 10}
    assert sorted(original ^ incorrect) == [10]
    return {'checked_quantities': '0〜20', 'different_at': sorted(original ^ incorrect),
            'limits': '意味評価、自然さ、製品比較は実行していない'}


def retrieval():
    source = {'DOC-001': {'version': 3, 'text': '原則5営業日以内。再発行待ちは経理へ相談',
                          'active': True, 'groups': ['employee', 'hr']},
              'DOC-002': {'version': 1, 'text': '旧備品購入手順', 'active': False, 'groups': ['employee']},
              'DOC-003': {'version': 2, 'text': '人事向け勤怠訂正', 'active': True, 'groups': ['hr']}}
    # 古い検索用データが残ることを意図的に再現する。
    index = {'DOC-001': {'version': 2, 'text': '10営業日以内'},
             'DOC-002': {'version': 1, 'text': '旧備品購入手順'},
             'DOC-003': {'version': 2, 'text': '人事向け勤怠訂正'}}
    def context(doc_id, group):
        current = source[doc_id]
        if not current['active'] or group not in current['groups']:
            return {'decision': '参照不可'}  # 題名・本文・引用を返さない。
        if index[doc_id]['version'] != current['version']:
            return {'decision': '更新待ち'}
        return {'decision': '参照可能', 'text': index[doc_id]['text']}
    before = context('DOC-001', 'employee')
    index['DOC-001'] = {'version': 3, 'text': source['DOC-001']['text']}
    after = context('DOC-001', 'employee')
    deleted, employee, hr = context('DOC-002', 'employee'), context('DOC-003', 'employee'), context('DOC-003', 'hr')
    assert before == {'decision': '更新待ち'}
    assert after['text'] == '原則5営業日以内。再発行待ちは経理へ相談'
    assert deleted == employee == {'decision': '参照不可'} and hr['decision'] == '参照可能'
    return {'stale_version': before, 'updated_version': after, 'retired': deleted,
            'restricted_employee': employee, 'restricted_hr': hr,
            'limits': '固定IDの参照模型。実検索、生成回答、キャッシュ、Azureの試験ではない'}


def ownership_cost():
    local = 450000 + 36 * (22000 + 16000)
    cloud = 36 * (31000 + 17000)
    threshold = D(cloud - 450000) / D(36)
    assert local == 1818000 and cloud == 1728000 and local - cloud == 90000
    assert threshold == D('35500')
    return {'local_hypothetical_yen': local, 'cloud_hypothetical_yen': cloud,
            'local_monthly_equal_cost_threshold_yen': str(threshold),
            'limits': '36か月の仮定による算術。実機・契約・性能の比較ではない'}


def energy():
    # Whを実測値として与えない。単位と増減の計算だけ。
    original_count, relative_per_task, new_count = D(10000), D('0.6'), D(20000)
    ratio = new_count * relative_per_task / original_count
    assert ratio == D('1.2')
    return {'hypothetical_per_task_change': '-40%', 'task_count_change': '+100%',
            'monthly_total_ratio': str(ratio), 'limits': '相対値の計算。製品やデータセンターの電力測定ではない'}


if __name__ == '__main__':
    print(json.dumps({'api': api_cost(), 'selection': selection(), 'schedule': schedule(),
                      'translation': translation(), 'retrieval': retrieval(), 'energy': energy(),
                      'ownership_cost': ownership_cost()},
                     ensure_ascii=False, indent=2))
