"""Read-only production functional checks; never emits tokens or profile data."""
import importlib.util
import argparse
import re
import json
from pathlib import Path
import subprocess

ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location('deployment_client', ROOT / 'scripts/deploy.py')
client = importlib.util.module_from_spec(spec)
spec.loader.exec_module(client)
config = client.configuration(ROOT / '.deploy/config.json')
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--release', default='b2351e7b71a4c78ea0b83ca43f2f8689963f2594')
parser.add_argument('--turn-enabled', action='store_true', help='Require authenticated ICE 200 after verified relay activation')
parser.add_argument('--login-compatible', action='store_true', help='Check unchanged QA login and disabled additive v2 route')
args = parser.parse_args()
revision = args.release
if not re.fullmatch('[a-f0-9]{40}', revision):
    raise ValueError('Full release revision required')
js = r'''
const fs=require('fs');
const dotenv=require('dotenv');
const phpSettings=dotenv.parse(fs.readFileSync('/www/wwwroot/app.connectlyvideochat.com/.env'));
const db=require('./db').promise;
const expected=__VERIFIED_RELEASE__;
const relayEnabled=__TURN_ENABLED__;
const loginCompatible=__LOGIN_COMPATIBLE__;
const result={};
async function get(url,token){
 const response=await fetch(url,{headers:token?{Authorization:token}:{},signal:AbortSignal.timeout(15000)});
 const data=await response.json();
 return {status:response.status,data,cache:response.headers.get('cache-control')};
}
function check(ok,label){if(!ok)throw Error(label);}
(async()=>{
 for(const [name,url] of [['local','http://127.0.0.1:3000'],['public','https://connectlyvideochat.com']]){
  const health=await get(url+'/health');check(health.status===200&&health.data.commit===expected,name+' health');
  result[name+'_health']={status:health.status,commit:health.data.commit};
 }
 const [[qa]]=await db.query('SELECT id,api_token,status,deleted_at,account_deletion_scheduled_at FROM users WHERE id=166828');
 check(qa&&qa.status===1&&!qa.deleted_at&&!qa.account_deletion_scheduled_at&&qa.api_token,'safe active QA identity');
 result.node_dependencies=Object.fromEntries(['express','mysql2','socket.io'].map(name=>[name,require('./node_modules/'+name+'/package.json').version]));
 const base='https://connectlyvideochat.com/api/webrtc/ice-servers';
 const unauthorized=await get(base+'?user_id='+qa.id);
 const wrong=await get(base+'?user_id='+qa.id,'Bearer invalid-round2-token');
 const authorized=await get(base+'?user_id='+qa.id,'Bearer '+qa.api_token);
 const invalid=await get(base+'?user_id=0','Bearer '+qa.api_token);
 check(unauthorized.status===401&&wrong.status===401&&invalid.status===400,'ICE validation');
 check(authorized.status===(relayEnabled?200:503)&&authorized.cache==='no-store','expected TURN availability');
 if(relayEnabled){
  const entry=authorized.data.ice_servers?.[0];
  check(entry&&Array.isArray(entry.urls)&&entry.urls.length>0&&typeof entry.credential==='string'&&
    entry.username.endsWith(':'+qa.id)&&authorized.data.ttl>=300,'temporary TURN credential response');
 }
 result.ice={without_token:401,wrong_token:401,invalid_id:400,authenticated:authorized.status,no_store:true,relay_enabled:relayEnabled};
 if(loginCompatible){
  const [[identity]]=await db.query('SELECT email FROM users WHERE id=?',[qa.id]);
  const [[duplicates]]=await db.query('SELECT COUNT(*) AS total FROM users WHERE email=?',[identity.email]);
  check(identity.email&&Number(duplicates.total)===1,'unique existing QA login identity');
  const login=await get('https://app.connectlyvideochat.com/userLoginGoogle?type=google&email='+
    encodeURIComponent(identity.email),phpSettings.API_KEY);
  check(login.status===200&&Number(login.data.user?.user_id)===qa.id&&
    !Object.hasOwn(login.data.user,'api_token')&&!Object.hasOwn(login.data,'token_type'),'legacy login preserved');
  const gate=await fetch('https://app.connectlyvideochat.com/v2/userLoginGoogle',{
   method:'POST',headers:{Authorization:phpSettings.API_KEY,'Content-Type':'application/json',Accept:'application/json'},
   body:JSON.stringify({type:'guest'}),signal:AbortSignal.timeout(15000)});
  const gated=await gate.json();
  check(gate.status===503&&gated.message==='Verified login is not enabled'&&
    gate.headers.get('cache-control')?.includes('no-store'),'v2 login safely gated');
  result.login={legacy:200,legacy_token_exposed:false,verified:503,verified_enabled:false};
 }
 const [[photo]]=await db.query("SELECT id FROM users WHERE status=1 AND deleted_at IS NULL AND account_deletion_scheduled_at IS NULL AND image IS NOT NULL AND image<>'' AND image NOT LIKE '%no-user.%' LIMIT 1");
 check(photo,'real photo control identity');
 for(const [name,url,token] of [['node','https://connectlyvideochat.com/api/getUserDetail',null],['legacy','https://app.connectlyvideochat.com/getUserDetail',phpSettings.API_KEY]]){
  const missing=await get(url+'?user_id='+qa.id,token);
  const actual=await get(url+'?user_id='+photo.id,token);
  check(missing.status===200&&missing.data.image===null,name+' missing photo');
  check(actual.status===200&&typeof actual.data.image==='string'&&actual.data.image.length>0&&!actual.data.image.includes('no-user.'),name+' real photo');
  result[name+'_photo']={missing_image:null,real_photo_preserved:true};
 }
 console.log('ROUND2_RESULT='+JSON.stringify(result));
})().catch(error=>{console.error('ROUND2_CHECK_FAILED='+error.message);process.exitCode=1}).finally(()=>db.end());
'''.replace('__VERIFIED_RELEASE__', json.dumps(revision)).replace('__TURN_ENABLED__', json.dumps(args.turn_enabled)) \
   .replace('__LOGIN_COMPATIBLE__', json.dumps(args.login_compatible))
code = """
import json, os, subprocess
binary='/www/server/nodejs/v20.19.2/bin'
env={**os.environ,'PATH':binary+':'+os.environ['PATH'],'PM2_HOME':'/root/.pm2'}
processes=json.loads(subprocess.check_output([binary+'/pm2','jlist'],text=True,env=env))
target=next(p for p in processes if p['name']=='videocall_server')
overrides={k:str(v) for k,v in target['pm2_env'].items() if k.startswith('DB_')}
result=subprocess.run([binary+'/node','-'],input=JS,text=True,capture_output=True,
 cwd='/www/wwwroot/connectlyvideochat.com/Videocall',env={**env,**overrides})
for line in (result.stdout+result.stderr).splitlines():
 if line.startswith(('ROUND2_RESULT=','ROUND2_CHECK_FAILED=')): print(line)
raise SystemExit(result.returncode)
""".replace('JS', repr(js))
result = subprocess.run(['ssh', '-p', str(config['port']), *client.ssh_options(config),
                         f"{config['user']}@{config['host']}", 'python3 -'], input=code, text=True, capture_output=True)
for line in result.stdout.splitlines():
    if line.startswith('ROUND2_RESULT='):
        data=json.loads(line.split('=',1)[1])
        (ROOT / '.deploy/round2-verification.json').write_text(json.dumps(data,indent=2)+'\n')
        print(json.dumps(data))
    elif line.startswith('ROUND2_CHECK_FAILED='):
        print(line)
if result.returncode:
    print('Read-only verification failed; no credentials emitted')
    raise SystemExit(result.returncode)
