import base64
import hashlib
import json
from pathlib import Path

import websocket


def receive(ws, expected_op):
    while True:
        message = json.loads(ws.recv())
        if message.get('op') == expected_op:
            return message['d']


def request(ws, request_type, request_data=None):
    request_id = request_type
    ws.send(json.dumps({
        'op': 6,
        'd': {
            'requestType': request_type,
            'requestId': request_id,
            'requestData': request_data or {},
        },
    }))
    response = receive(ws, 7)
    status = response['requestStatus']
    if not status['result']:
        raise RuntimeError(f"{request_type}: {status.get('comment', 'failed')}")
    return response.get('responseData', {})


config_path = Path.home() / 'AppData/Roaming/obs-studio/plugin_config/obs-websocket/config.json'
config = json.loads(config_path.read_text(encoding='utf-8-sig'))
port = config.get('server_port', 4455)

ws = websocket.create_connection(f'ws://127.0.0.1:{port}', timeout=5)
hello = receive(ws, 0)
identify = {'rpcVersion': hello['rpcVersion']}
authentication = hello.get('authentication')
if authentication:
    secret = base64.b64encode(hashlib.sha256(
        (config['server_password'] + authentication['salt']).encode()
    ).digest()).decode()
    identify['authentication'] = base64.b64encode(hashlib.sha256(
        (secret + authentication['challenge']).encode()
    ).digest()).decode()

ws.send(json.dumps({'op': 1, 'd': identify}))
receive(ws, 2)

inputs = request(ws, 'GetInputList')['inputs']
preferred = sorted(
    (item['inputName'] for item in inputs),
    key=lambda name: ('tibia' not in name.lower(), name.lower()),
)

output = Path.home() / 'TibiaPilotNG/.obs-tibia.png'
errors = []
for source_name in preferred:
    try:
        data = request(ws, 'GetSourceScreenshot', {
            'sourceName': source_name,
            'imageFormat': 'png',
            'imageWidth': 1920,
            'imageHeight': 1080,
            'imageCompressionQuality': -1,
        })['imageData']
        output.write_bytes(base64.b64decode(data.split(',', 1)[1]))
        print(json.dumps({'ok': True, 'source': source_name, 'file': str(output)}))
        break
    except Exception as error:
        errors.append(f'{source_name}: {error}')
else:
    raise RuntimeError('; '.join(errors))

ws.close()
