"""Actual TLS + authenticated synthetic device, fake STT explicitly labelled."""
import asyncio
import base64
import json
import os
from pathlib import Path
import socket
import ssl
import subprocess
import tempfile
import sys
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

import httpx
import uvicorn
from websockets.asyncio.client import connect

from test_voice_settings import FakeAgent, FakeSTT
import device_gateway.app as gateway
from device_gateway.auth import DeviceCredentialStore
from device_gateway.control.models import Project
from device_gateway.control.projects import ProjectRegistry
from device_gateway.state import TargetStore


async def main():
    with tempfile.TemporaryDirectory(prefix='device-voice-wss-') as folder:
        root=Path(folder)
        os.environ['DEVICE_LAB_CONTROL_DIR']=str(root/'runtime')
        credential=root/'credentials.json'
        credential.write_text(json.dumps(dict(device_id='synthetic',device_token='local-test-only')))
        credential.chmod(0o600)
        gateway.credential_store=DeviceCredentialStore(credential)
        gateway.target_store=TargetStore(root/'targets.json')
        gateway.project_registry=ProjectRegistry(root/'projects.json')
        gateway.project_registry.add(Project(id='p',name='Synthetic',path=folder))
        gateway.adapters={a:FakeAgent(a,folder) for a in ['pi','codex']}
        gateway.voices.provider=FakeSTT()
        gateway.devices={}
        cert,key=root/'cert.pem',root/'key.pem'
        subprocess.run(['openssl','req','-x509','-newkey','rsa:2048','-nodes','-days','1',
            '-subj','/CN=localhost','-addext','subjectAltName=DNS:localhost,IP:127.0.0.1',
            '-keyout',str(key),'-out',str(cert)],check=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
        context=ssl.create_default_context(cafile=str(cert))
        with socket.socket() as sock:sock.bind(('127.0.0.1',0));port=sock.getsockname()[1]
        server=uvicorn.Server(uvicorn.Config(gateway.app,host='127.0.0.1',port=port,log_level='error',
            ssl_certfile=str(cert),ssl_keyfile=str(key),ws_max_size=8192))
        task=asyncio.create_task(server.serve())
        async def receive(ws,kind):
            async with asyncio.timeout(10):
                while True:
                    value=json.loads(await ws.recv())
                    if value['type']==kind:return value
        try:
            async with asyncio.timeout(10):
                while not server.started:await asyncio.sleep(.01)
            async with httpx.AsyncClient(base_url=f'https://127.0.0.1:{port}',verify=context) as client:
                async with connect(f'wss://127.0.0.1:{port}/device/ws',ssl=context) as ws:
                    await ws.send(json.dumps(dict(type='hello',device_id='synthetic',device_token='local-test-only',board='synthetic',firmware='0.4.0')))
                    await receive(ws,'hello_ack');await receive(ws,'target_state')
                    for agent in ('pi','codex'):
                        response=await client.post('/devices/synthetic/target',json={'target':f'agent:{agent}:s'})
                        response.raise_for_status()
                        assert (await receive(ws,'agent_state'))['agent']==agent
                        await ws.send(json.dumps(dict(type='voice_start',voice_id=agent,sample_rate=44100,bits=16,channels=1)))
                        await receive(ws,'voice_state')
                        for seq in range(3):
                            await ws.send(json.dumps(dict(type='voice_chunk',voice_id=agent,sequence=seq,audio=base64.b64encode(b'\0\0'*1764).decode())))
                        await ws.send(json.dumps(dict(type='voice_end',voice_id=agent)))
                        transcript=await receive(ws,'voice_transcript')
                        assert gateway.adapters[agent].inputs==[]
                        await ws.send(json.dumps(dict(type='voice_decision',voice_id=agent,decision='send')))
                        assert (await receive(ws,'voice_state'))['state']=='SENT'
                        assert gateway.adapters[agent].inputs==[('s',transcript['transcript'])]
                        print(f'{agent.upper()} authenticated TLS WSS voice transport + FAKE STT + confirmed input: PASS')
        finally:
            server.should_exit=True
            await asyncio.wait_for(task,10)


if __name__=='__main__':asyncio.run(main())
