#!/usr/bin/env python3
"""Validate an isolated Laravel login candidate and real Node ICE handoff on synthetic localhost data only."""
import argparse
import hashlib
import os
from pathlib import Path
import socket
import subprocess
import tempfile
import time

ROOT = Path(__file__).resolve().parents[1]


def validate(candidate, source, node_binary):
    candidate, source = candidate.resolve(), source.resolve()
    if candidate == source or source in candidate.parents:
        raise ValueError('Candidate must be isolated from the original checkout')
    expected = 'b1a6e44a052c764d63ad5284e06200d17b61c56af4bccea9a8d90d8493456b3c'
    for folder in [source, candidate]:
        if hashlib.sha256((folder / 'composer.lock').read_bytes()).hexdigest() != expected:
            raise ValueError('Composer lock differs from the reviewed production dependency baseline')
    if (candidate / '.env').exists() or (candidate / 'bootstrap/cache/config.php').exists():
        raise ValueError('Test candidate must have no environment file or cached runtime configuration')
    environment = {
        **os.environ, 'BONDVIA_TEST_DATABASE': '1', 'DB_HOST': '127.0.0.1', 'DB_PORT': '13316',
        'DB_USER': 'root', 'DB_PASSWORD': '', 'DB_NAME': 'bondvia_mobile_test',
        'TURN_ENABLED': '1', 'TURN_HOST': 'turn.test.example',
        'TURN_SHARED_SECRET': 'local-test-secret-never-production-12345',
        'TURN_CREDENTIAL_TTL_SECONDS': '3600',
        'TURN_URLS': 'turn:turn.test.example:3478?transport=udp',
    }
    node_binary = node_binary.resolve()
    environment['PATH'] = str(node_binary.parent) + os.pathsep + os.environ['PATH']
    print(subprocess.check_output([str(node_binary), '--version'], text=True).strip(), flush=True)
    # test:mobile resets only its guarded synthetic database, migrates twice and exercises actual HTTP/Socket.IO APIs.
    subprocess.run(['npm', 'test'], cwd=ROOT, env={**environment, 'BONDVIA_TEST_DATABASE': '0'}, check=True)
    subprocess.run(['npm', 'run', 'test:mobile'], cwd=ROOT, env=environment, check=True)
    php_env = {
        'APP_ENV': 'testing', 'APP_DEBUG': 'false', 'APP_KEY': 'base64:' + 'A' * 43 + '=',
        'DB_CONNECTION': 'mysql', 'DB_HOST': '127.0.0.1', 'DB_PORT': '13316',
        'DB_DATABASE': 'bondvia_mobile_test', 'DB_USERNAME': 'root', 'DB_PASSWORD': '',
        'CACHE_DRIVER': 'array', 'SESSION_DRIVER': 'array', 'MAIL_MAILER': 'array',
        'QUEUE_CONNECTION': 'sync', 'API_KEY': 'local-test-bootstrap',
        'SOCIAL_LOGIN_ICE_TEST_URL': 'http://127.0.0.1:13998',
    }
    command = ['docker', 'run', '--rm', '--network', 'host', '--user', f'{os.getuid()}:{os.getgid()}',
               '-v', f'{candidate}:/app', '-v', f'{source / "vendor"}:/app/vendor:ro']
    for key, value in php_env.items():
        command.extend(['-e', f'{key}={value}'])
    command.extend(['bondvia-php-test:8.3', 'php', 'vendor/bin/phpunit', '--filter', 'SocialLogin'])
    with tempfile.TemporaryFile(mode='w+') as log:
        node = subprocess.Popen([str(node_binary), 'tests/social-login-ice.js'], cwd=ROOT, env=environment,
                                stdout=log, stderr=log)
        try:
            for _ in range(50):
                if node.poll() is not None:
                    raise RuntimeError('Local ICE fixture stopped before testing')
                try:
                    with socket.create_connection(('127.0.0.1', 13998), timeout=0.2):
                        break
                except OSError:
                    time.sleep(0.1)
            else:
                raise RuntimeError('Local ICE fixture did not start')
            subprocess.run(command, check=True)
        finally:
            node.terminate()
            try:
                node.wait(timeout=10)
            except subprocess.TimeoutExpired:
                node.kill()
                node.wait()
    print('Login, migration, real middleware and login-to-ICE gates passed; no production services were used.')


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--candidate', type=Path, required=True)
    parser.add_argument('--source', type=Path, required=True)
    parser.add_argument('--node-binary', type=Path, default=Path('/usr/bin/node'))
    args = parser.parse_args()
    validate(args.candidate, args.source, args.node_binary)
