"""Read-only reporting must survive disappearance/restore without new history."""
import json
import shutil
from fastapi.testclient import TestClient
from backend import recovery
from backend.app import create_app
from backend.reporting import Filters, ExportFilters, build_report, export_csv
from backend.store import now, DEMO_BUSINESSES
from test_boundaries import client, write, job, quote, ORIGIN
from test_documents import act
from test_fieldwork import captured
from test_workforce import entry
from test_materials import material
from test_workflow import edit
from test_customer_reviews import selection, preview, confirm
from test_identity import new_business


def run_reporting_drill(tmp_path):
    source = tmp_path / 'fictional'; application = create_app(source); owner = client(application)
    clients = {}; ids = {}
    for n, slug in enumerate(('oak-and-vale', 'harbour-homeworks', 'willow-reporting', 'cedar-reporting')):
        if n < 2:
            c = client(application, n); bid = DEMO_BUSINESSES[n]['id']
        else:
            business = new_business(owner, slug).json(); bid = business['business_id']
            switched = write(owner, '/api/business/switch', dict(membership_id=business['membership_id']))
            owner.headers['x-csrf-token'] = switched.json()['csrf']; c = owner
        j, people, clock, tid = captured(c, application)
        entry(c, j, people[0], work_date='2026-10-15', duration_minutes=30)
        material(c, j, quantity='2', cost_price='12.34', status='received', received_date='2026-10-15')
        q = quote(c, j, 10000)
        assert act(c, 'quote', q, 'share').status_code == 200
        assert act(c, 'quote', q, 'accept').status_code == 200
        i = act(c, 'quote', q, 'invoice').json()
        assert act(c, 'invoice', i, 'issue').status_code == 200
        assert write(c, '/api/invoices/' + i['id'] + '/payments', dict(amount_pence=1000, paid_on='2026-10-15')).status_code == 200
        edit(c, j, status='completed', completed_date='2026-10-15', tags=['reviewed', 'repeat'])
        target = job(c, name='Retained Fictional Customer'); twin = job(c)
        assert confirm(c, preview(c, selection(c, j, target))).status_code == 200
        clients[bid] = c; ids[bid] = dict(original=j['customer_id'], retained=target['customer_id'], distinct=twin['customer_id'])
    observation = now(); filters = Filters(start='2026-01-01', end='2026-12-31'); before = {}; files = {}
    for bid in clients:
        with application.state.store.tenant(bid) as db:
            db.execute('PRAGMA query_only=ON'); db.execute('BEGIN')
            result = build_report(db, filters, generated_at=observation)
        assert result['totals']['issued_invoice_value_pence'] == 10000
        assert result['totals']['recorded_receipts_pence'] == 1000
        assert result['totals']['issued_invoice_balance_pence'] == 9000
        assert result['totals']['person_minutes'] == 32 and result['totals']['internal_labour_cost_pence'] == 1067
        assert result['totals']['received_material_cost_pence'] == 2468
        assert len(result['options']['customers']) == 3 and len(result['groups']['customer']) == 2
        choice = ExportFilters(**filters.model_dump(), snapshot=result['snapshot'], dimension='customer')
        before[bid] = result; files[bid] = export_csv(result, choice, bid)
    archive = tmp_path / 'reporting.zip'; saved = recovery.backup(source, archive)
    assert recovery.verify(archive, saved['sha256'])['valid'] and recovery.VERSION == 14
    shutil.rmtree(source)  # disposable invented source only; prove recovery after disappearance
    target = tmp_path / 'restored'; recovery.restore(archive, saved['sha256'], target, confirm_fresh=True)
    restored = create_app(target)
    for bid, result in before.items():
        with restored.state.store.tenant(bid) as db:
            db.execute('PRAGMA query_only=ON'); db.execute('BEGIN')
            after = build_report(db, filters, generated_at=observation)
        assert result == after
        assert export_csv(after, ExportFilters(**filters.model_dump(), snapshot=after['snapshot'], dimension='customer'), bid) == files[bid]
        foreign = next(v for k, v in ids.items() if k != bid)
        assert all(r['customer_id'] not in foreign.values() for r in after['records'])
        stale = TestClient(restored, headers=ORIGIN); stale.cookies.update(clients[bid].cookies)
        assert stale.get('/api/reports/activity', params=filters.model_dump()).status_code == 401
    second = recovery.backup(target, tmp_path / 'second.zip')
    assert recovery.verify(second['archive'], second['sha256'])['valid']
    return dict(businesses=4, source_deleted_before_restore=True, recovery_format=11, schema_change=False,
                reports_and_export_bytes_identical_at_same_observation=True, no_historical_events_invented=True,
                customer_transfers=4, original_document_identities_preserved=True, stale_sessions_rejected=True,
                per_business_expected=dict(issued_pence=10000, receipts_pence=1000, balance_pence=9000, person_minutes=32, labour_pence=1067, received_materials_pence=2468),
                report_snapshots={bid:r['snapshot'] for bid,r in before.items()}, archive_sha256=saved['sha256'], second_generation_verified=True)


def test_four_business_reports_and_exports_survive_disappearance(tmp_path):
    result = run_reporting_drill(tmp_path)
    assert result['businesses'] == 4 and result['reports_and_export_bytes_identical_at_same_observation']
