#!/usr/bin/env python3
"""Validate one read-only Search Console report, without making network requests.

This checks a saved report. It does not test an LLM, production permissions,
security controls or the performance of an autonomous agent.
"""
import argparse
import copy
import hashlib
import json
from pathlib import Path

DEFAULT_URLS = {
    'https://outproduce.io/best/best-ai-employee-providers',
    'https://outproduce.io/best/best-ai-automation-agencies',
    'https://outproduce.io/best/best-ai-agent-agencies',
}


def validate(report, expected_urls):
    errors = []
    inspection = report.get('inspection', [])
    summary = report.get('summary', {})
    urls = [row.get('url') for row in inspection]
    if len(urls) != len(set(urls)) or set(urls) != set(expected_urls):
        errors.append('inspection must contain every expected URL exactly once')
    if summary.get('requested_url_count') != len(expected_urls) or summary.get('inspected_url_count') != len(inspection):
        errors.append('declared URL counts must match the request and returned rows')
    if any(row.get('verdict') != 'PASS' for row in inspection):
        errors.append('at least one expected page lacks a PASS inspection verdict')
    if any(row.get('googleCanonical') != row.get('url') or row.get('userCanonical') != row.get('url') for row in inspection):
        errors.append('canonical URLs must match each inspected URL')
    response = report.get('performance', {}).get('response', {})
    rows = response.get('rows', [])
    if any(len(row.get('keys', [])) != 2 or row['keys'][1] not in expected_urls for row in rows):
        errors.append('performance rows must belong to the expected URLs')
    if summary.get('performance_rows') != len(rows):
        errors.append('declared performance row count must match the saved rows')
    for metric in ['clicks', 'impressions']:
        if summary.get(metric) != sum(row.get(metric, 0) for row in rows):
            errors.append('declared '+metric+' must reconcile with the saved rows')
    metadata = response.get('metadata', {})
    if summary.get('metadata') != metadata:
        errors.append('summary freshness metadata must match the API response')
    if metadata.get('firstIncompleteDate') and 'incomplete' not in summary.get('data_state', '').lower():
        errors.append('incomplete data must be labelled in the summary')
    scope = report.get('scope', {})
    if scope.get('external_writes') != 0 or scope.get('changes_need_separate_approval') is not True:
        errors.append('this report must declare read-only scope and separate change approval')
    return errors


def self_test(report, expected_urls):
    baseline_errors = validate(report, expected_urls)
    if baseline_errors:
        raise ValueError('The original saved report fails: '+str(baseline_errors))
    mutations = []
    def case(name, change):
        altered = copy.deepcopy(report)
        change(altered)
        errors = validate(altered, expected_urls)
        mutations.append({'case': name, 'input_kind': 'synthetic mutation of a saved report', 'rejected': bool(errors), 'errors': errors})
    case('missing expected URL', lambda d: d['inspection'].pop())
    case('duplicated inspected URL', lambda d: d['inspection'].__setitem__(1, copy.deepcopy(d['inspection'][0])))
    case('index verdict is not PASS', lambda d: d['inspection'][0].__setitem__('verdict', 'NEUTRAL'))
    case('canonical points to another URL', lambda d: d['inspection'][0].__setitem__('googleCanonical', 'https://example.invalid/wrong'))
    case('performance row belongs to another site', lambda d: d['performance']['response']['rows'][0]['keys'].__setitem__(1, 'https://example.invalid/unrelated'))
    case('summary total does not reconcile', lambda d: d['summary'].__setitem__('impressions', d['summary']['impressions']+1))
    case('incomplete window labelled complete', lambda d: d['summary'].__setitem__('data_state', 'complete'))
    case('write reported inside read-only scope', lambda d: d['scope'].__setitem__('external_writes', 1))
    return {'original_report_passed': True, 'synthetic_cases': mutations, 'all_synthetic_cases_rejected': all(c['rejected'] for c in mutations)}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('receipt', type=Path)
    parser.add_argument('--expected-url', action='append', dest='urls')
    parser.add_argument('--self-test', action='store_true')
    args = parser.parse_args()
    raw = args.receipt.read_bytes()
    report = json.loads(raw)
    expected_urls = set(args.urls) if args.urls else DEFAULT_URLS
    result: dict[str, object] = self_test(report, expected_urls) if args.self_test else {'errors': validate(report, expected_urls)}
    result['input_sha256'] = hashlib.sha256(raw).hexdigest()
    result['scope'] = 'Saved-report validation only. No network requests, LLM evaluation, production-write test or security certification.'
    print(json.dumps(result, indent=2))
    ok = result.get('all_synthetic_cases_rejected', not result.get('errors'))
    raise SystemExit(0 if ok else 1)


if __name__ == '__main__':
    main()
