"""Foresixa one-shot paper account and fresh crypto REST quote diagnostic.

No WebSocket stream, futures provider, ledger writes, orders or live clearance.
Configuration labels are declarations only and never connectivity evidence.
"""
import argparse
import json
import os
from datetime import datetime, timezone
from pathlib import Path
from urllib.parse import urlencode

from secure_credential_validator import SecureCredentialValidator, load_private_credentials
from check_live_status import PAPER, DATA, account_credentials, read_json, Rejected
from engine import instant, number


def unique_object(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise Rejected('Duplicate configuration key')
        result[key] = value
    return result


def load_worker_config(path):
    with open(path, 'rb') as stream:
        raw = stream.read(16385)
    if len(raw) > 16384:
        raise Rejected('Configuration too large')
    config = json.loads(raw, object_pairs_hook=unique_object,
                        parse_constant=lambda _: (_ for _ in ()).throw(Rejected('Nonfinite configuration')))
    validate_config(config)
    return config


def validate_config(config):
    if not isinstance(config, dict) or set(config) != {'foresixa_version', 'ingestion_lifecycle', 'active_worker_streams'}:
        raise Rejected('Invalid configuration structure')
    if not isinstance(config['foresixa_version'], str):
        raise Rejected('Invalid configuration version')
    ingress = config['ingestion_lifecycle']
    if ingress != {'account_mode': 'PAPER_SANDBOX', 'verified_provider_endpoint': PAPER}:
        raise Rejected('Only the canonical paper account endpoint is supported')
    workers = config['active_worker_streams']
    if not isinstance(workers, dict) or set(workers) != {'crypto_worker', 'futures_worker'}:
        raise Rejected('Explicit crypto and futures configuration required')
    crypto, futures = workers['crypto_worker'], workers['futures_worker']
    if not isinstance(crypto, dict) or set(crypto) != {'stream_feed', 'symbols_monitored'}:
        raise Rejected('Invalid crypto configuration')
    symbols = crypto['symbols_monitored']
    if (not isinstance(symbols, list) or not symbols or
            any(not isinstance(s, str) or s not in ('BTC/USD', 'ETH/USD', 'SOL/USD') for s in symbols) or
            len(set(symbols)) != len(symbols)):
        raise Rejected('Unsupported or duplicate crypto symbol')
    if not isinstance(crypto['stream_feed'], str):
        raise Rejected('Invalid declared feed label')
    if (not isinstance(futures, dict) or set(futures) != {'stream_feed', 'symbols_monitored', 'official_cme_point_multiplier'} or
            futures['symbols_monitored'] != ['/MES'] or not isinstance(futures['stream_feed'], str) or
            isinstance(futures['official_cme_point_multiplier'], bool) or
            number(futures['official_cme_point_multiplier'], True) != 5):
        raise Rejected('MES must declare a 5 USD per index point contract multiplier')


def validate_quotes(payload, symbols, now):
    if not isinstance(payload, dict) or not isinstance(payload.get('quotes'), dict) or set(payload['quotes']) != set(symbols):
        raise Rejected('Missing requested quote observations')
    observations = {}
    for symbol in symbols:
        quote = payload['quotes'][symbol]
        if not isinstance(quote, dict):
            raise Rejected('Malformed quote')
        bid, ask = number(quote['bp'], True), number(quote['ap'], True)
        if bid > ask:
            raise Rejected('Crossed quote')
        at = instant(quote['t'])
        age = (now - at).total_seconds()
        if not -5 <= age <= 60:
            raise Rejected('Stale or future quote')
        observations[symbol] = {'bid': str(bid), 'ask': str(ask),
                                'provider_timestamp': at.isoformat(), 'age_seconds': age}
    return observations


class ForesixaLiveFeedVerifier:
    def __init__(self, config_path=None, *, config=None, get=read_json,
                 clock=lambda: datetime.now(timezone.utc)):
        self.config_path = config_path or Path(__file__).with_name('worker_config.json')
        self.config = config
        self.get, self.clock = get, clock

    def execute_exchange_stream_validation(self, *, settings=None,
                                           credentials_path=None, environment_only=False):
        report = {
            'engine_architecture': 'FORESIXA_VALIDATION_CORE',
            'execution_timestamp': self.clock().isoformat(),
            'status': 'CONFIGURATION_BLOCKED',
            'authentication_lifecycle': {'status': 'NOT_VERIFIED', 'account_mode': 'PAPER_SANDBOX'},
            'network_peripheral_gates': {'broker_handshake_clear': False,
                'market_data_connectivity': 'NOT_VERIFIED', 'active_order_submission_allowed': False},
            'multi_worker_feed_registry': {'crypto_feed_status': 'NOT_VERIFIED',
                'futures_feed_status': 'FUTURES_FEED_UNCONFIGURED',
                'stocks_options_feed_status': 'NOT_CHECKED'},
            'stream_verified': False, 'broker_orders_submitted': 0,
            'trial_started': False, 'live_clearance_granted': False,
            'credential_rotation_verified': False,
        }
        try:
            config = self.config if self.config is not None else load_worker_config(self.config_path)
            validate_config(config)
            if credentials_path is not None and environment_only:
                raise Rejected('Select one credential source')
            if settings is not None:
                if credentials_path is not None or environment_only:
                    raise Rejected('Select one credential source')
                private = dict(settings)
            elif environment_only:
                private = dict(os.environ)
            else:
                private = load_private_credentials(credentials_path or Path(__file__).with_name('credentials.env'))
            # Credential-file endpoint conflicts fail; configuration cannot redirect secret headers.
            base, headers = account_credentials(private, 'paper')
            if base != config['ingestion_lifecycle']['verified_provider_endpoint']:
                raise Rejected('Endpoint mismatch')
        except Exception:
            report['reason'] = 'Check worker config, private credential file, paper credential pair and matching endpoint'
            return json.dumps(report, indent=2, allow_nan=False)

        report['status'] = 'CHECKS_NOT_VERIFIED'
        auth = SecureCredentialValidator(mode='paper', settings=private,
            environment_only=True, get=self.get).authentication_report()
        authenticated = auth['status'] == 'ACCOUNT_READ_AUTHENTICATED'
        report['authentication_lifecycle']['status'] = auth['status']
        report['network_peripheral_gates']['broker_handshake_clear'] = authenticated
        symbols = config['active_worker_streams']['crypto_worker']['symbols_monitored']
        quote_url = DATA + '/v1beta3/crypto/us/latest/quotes?' + urlencode({'symbols': ','.join(symbols)})
        try:
            observations = validate_quotes(self.get(quote_url, headers), symbols, self.clock())
            report['crypto_observations'] = {'source_endpoint': DATA,
                'retrieved_at': self.clock().isoformat(), 'quotes': observations}
            report['network_peripheral_gates']['market_data_connectivity'] = 'FRESH_CRYPTO_REST_QUOTES_VERIFIED'
            report['multi_worker_feed_registry']['crypto_feed_status'] = 'FRESH_REST_QUOTES_VERIFIED'
            if authenticated:
                report['status'] = 'PARTIAL_CHECKS_PASSED_FUTURES_UNCONFIGURED'
        except Exception:
            report['crypto_quote_reason'] = 'Crypto request rejected, unavailable, incomplete, stale or malformed'
        # Never print credential fragments, broker account contents or arbitrary exception/server text.
        return json.dumps(report, indent=2, allow_nan=False)


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--config', default=str(Path(__file__).with_name('worker_config.json')))
    source = parser.add_mutually_exclusive_group()
    source.add_argument('--credentials-file', help='Private literal credential file; paper credentials only')
    source.add_argument('--environment-only', action='store_true')
    args = parser.parse_args()
    result = ForesixaLiveFeedVerifier(args.config).execute_exchange_stream_validation(
        credentials_path=args.credentials_file, environment_only=args.environment_only)
    print(result)
    # Multi-worker verification remains incomplete while the futures provider is unconfigured.
    return 1


if __name__ == '__main__':
    raise SystemExit(main())
