#!/usr/bin/env bash
# OAG Conformance Adapter - Python SDK
# Communicates with the runner via stdin/stdout JSON-line protocol.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

PYTHON="python3"
SDK_DIR="$SCRIPT_DIR/../../../sdks/python"
if [ -d "$SDK_DIR" ]; then
    SDK_DIR="$(cd "$SDK_DIR" && pwd)"
    if [ -f "$SDK_DIR/.venv/bin/python3" ]; then
        PYTHON="$SDK_DIR/.venv/bin/python3"
    fi
    export PYTHONPATH="$SDK_DIR:${PYTHONPATH:-}"
fi

exec "$PYTHON" -u -c "
import json
import sys

import blake3
import nacl.signing

def calculate_sekuire_id(model, system_prompt, tools):
    prompt_hash = blake3.blake3(system_prompt.strip().encode('utf-8')).hexdigest()
    try:
        canonical_tools = json.dumps(json.loads(tools), separators=(',', ':'))
    except (json.JSONDecodeError, TypeError):
        canonical_tools = tools
    tools_hash = blake3.blake3(canonical_tools.encode('utf-8')).hexdigest()
    fingerprint = f'model:{model}|prompt:{prompt_hash}|tools:{tools_hash}'
    return blake3.blake3(fingerprint.encode('utf-8')).hexdigest()


def sign(message, secret_key_hex):
    key_bytes = bytes.fromhex(secret_key_hex.strip())
    if len(key_bytes) == 64:
        seed = key_bytes[:32]
    elif len(key_bytes) == 32:
        seed = key_bytes
    else:
        raise ValueError(f'unexpected key length: {len(key_bytes)}')
    signing_key = nacl.signing.SigningKey(seed)
    signed = signing_key.sign(message.encode('utf-8'))
    return signed.signature.hex()


def verify(message, signature_hex, public_key_hex):
    pub_bytes = bytes.fromhex(public_key_hex)
    sig_bytes = bytes.fromhex(signature_hex)
    verify_key = nacl.signing.VerifyKey(pub_bytes)
    try:
        verify_key.verify(message.encode('utf-8'), sig_bytes)
        return True
    except nacl.exceptions.BadSignatureError:
        return False


def match_domain(pattern, domain):
    if pattern == '*':
        return True
    if pattern.startswith('*.'):
        suffix = pattern[2:]
        return domain == suffix or domain.endswith('.' + suffix)
    return pattern == domain


def match_path(pattern, path):
    if pattern.endswith('/*'):
        prefix = pattern[:-1]
        return path.startswith(prefix)
    return pattern == path


def enforce_policy(policy, action):
    content = policy.get('content', {})
    action_type = action.get('type', '')

    if action_type in ('network', 'network_access'):
        net = content.get('permissions', {}).get('network')
        if not net:
            return 'allow'
        if net.get('enabled') is False:
            return 'deny'
        if action.get('protocol') == 'http' and net.get('require_tls'):
            return 'deny'
        domain = action.get('domain', '')
        blocked = net.get('blocked_domains', [])
        if any(match_domain(d, domain) for d in blocked):
            return 'deny'
        allowed = net.get('allowed_domains', [])
        if allowed and not any(match_domain(d, domain) for d in allowed):
            return 'deny'
        return 'allow'

    if action_type in ('filesystem', 'file_access'):
        fs = content.get('permissions', {}).get('filesystem')
        if not fs:
            return 'allow'
        if fs.get('enabled') is False:
            return 'deny'
        path = action.get('path', '')
        blocked = fs.get('blocked_paths', [])
        if any(match_path(p, path) for p in blocked):
            return 'deny'
        allowed = fs.get('allowed_paths', [])
        if allowed and not any(match_path(p, path) for p in allowed):
            return 'deny'
        return 'allow'

    if action_type in ('tool', 'tool_execution'):
        tools = content.get('tools')
        if not tools:
            return 'allow'
        name = action.get('toolName') or action.get('tool_name', '')
        if name in tools.get('blocked_tools', []):
            return 'deny'
        allowed = tools.get('allowed_tools', [])
        if allowed and not any(t.get('name') == name for t in allowed):
            return 'deny'
        return 'allow'

    if action_type in ('model', 'model_call'):
        models = content.get('agent', {}).get('models')
        if not models:
            return 'allow'
        name = action.get('model') or action.get('model_name', '')
        if name in models.get('blocked_models', []):
            return 'deny'
        allowed = models.get('allowed_models', [])
        if allowed and name not in allowed:
            return 'deny'
        return 'allow'

    if action_type in ('api', 'api_access'):
        api = content.get('permissions', {}).get('api')
        if not api:
            return 'allow'
        if api.get('enabled') is False:
            return 'deny'
        service = action.get('service') or action.get('service_name', '')
        allowed = api.get('allowed_services', [])
        if allowed and not any(s.get('service_name') == service for s in allowed):
            return 'deny'
        return 'allow'

    if action_type in ('rate_limit', 'rate_limit_check'):
        limits = content.get('rate_limits', {}).get('per_agent')
        if not limits:
            return 'allow'
        count = action.get('count', 0)
        rpm = action.get('current_rpm', count)
        rph = action.get('current_rph', 0)
        max_rpm = limits.get('requests_per_minute')
        max_rph = limits.get('requests_per_hour')
        if max_rpm is not None and rpm > max_rpm:
            return 'deny'
        if max_rph is not None and rph > max_rph:
            return 'deny'
        return 'allow'

    return 'allow'


for line in sys.stdin:
    line = line.strip()
    if not line:
        continue
    try:
        cmd = json.loads(line)
    except json.JSONDecodeError as e:
        print(json.dumps({'error': f'invalid JSON: {e}'}), flush=True)
        continue

    op = cmd.get('operation', '')
    params = cmd.get('params', {})

    if op == 'exit':
        print(json.dumps({'result': 'ok'}), flush=True)
        break

    try:
        if op == 'calculate_sekuire_id':
            result = calculate_sekuire_id(params['model'], params['systemPrompt'], params['tools'])
        elif op == 'sign':
            result = sign(params['message'], params['secretKey'])
        elif op == 'verify':
            result = verify(params['message'], params['signature'], params['publicKey'])
        elif op == 'enforce_policy':
            result = enforce_policy(params['policy'], params['action'])
        elif op == 'hash':
            result = blake3.blake3(params['input'].encode('utf-8')).hexdigest()
        else:
            print(json.dumps({'error': f'unknown operation: {op}'}), flush=True)
            continue

        print(json.dumps({'result': result}), flush=True)
    except Exception as e:
        print(json.dumps({'error': str(e)}), flush=True)
"
