"""Local stand-ins for the model under test and the reward-model gateway, for end-to-end tests of the Worker.
Every request is appended to requests.jsonl so the test can compare what the engine sent with the reference.

  /v1/chat/completions  deterministic reply derived from the last message (sometimes multi-line)
  /v1/models            one model
  /v1/rewards           score = 1 + (sha256(text) mod 400) / 100, i.e. in [1, 5)
"""
import hashlib
import json
import os
import sys
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

LOG = sys.argv[2]


class H(BaseHTTPRequestHandler):
    def log_message(self, *a):
        pass

    def _json(self, code, obj):
        body = json.dumps(obj, ensure_ascii=False).encode()
        self.send_response(code)
        self.send_header('content-type', 'application/json')
        self.send_header('content-length', str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def do_GET(self):
        if self.path == '/e2e-cred':
            # Local test credentials for the browser check (localhost only).
            body = open(os.path.join(os.path.dirname(LOG), 'local_cred.json'), 'rb').read()
            self.send_response(200)
            self.send_header('access-control-allow-origin', 'http://localhost:5175')
            self.send_header('content-length', str(len(body)))
            self.end_headers()
            self.wfile.write(body)
            return
        if self.path.endswith('/models'):
            return self._json(200, {'data': [{'id': 'mock-model', 'name': 'mock-model'}, {'id': 'baichuan-char-rm', 'name': 'baichuan-char-rm'}]})
        self._json(404, {'error': {'message': 'not found'}})

    def do_POST(self):
        n = int(self.headers.get('content-length', 0))
        req = json.loads(self.rfile.read(n) or b'{}')
        with open(LOG, 'a', encoding='utf-8') as f:
            f.write(json.dumps({'path': self.path, 'body': req}, ensure_ascii=False) + '\n')
        if self.path.endswith('/rewards'):
            data = []
            for i, x in enumerate(req.get('inputs', [])):
                text = x['messages'][0]['content']
                h = int(hashlib.sha256(text.encode()).hexdigest(), 16)
                data.append({'object': 'reward', 'index': i, 'score': 1 + (h % 400) / 100})
            return self._json(200, {'object': 'list', 'model': req.get('model'), 'data': data})
        if self.path.endswith('/chat/completions') and self.coser(req):
            return
        if self.path.endswith('/chat/completions'):
            msgs = req.get('messages', [])
            last = msgs[-1]['content'] if msgs else ''
            # Fault injection: the first user message decides (it holds the character profile, fixed per character).
            first = msgs[0]['content'] if msgs else ''
            if os.environ.get('FAULTS') and '扮演佟湘玉进行对话' in first:
                return self._json(400, {'error': {'message': 'Provider returned error', 'code': 400, 'metadata': {'raw': 'The response was filtered due to the prompt triggering content management policy', 'provider_error_code': 'content_filter'}}})
            if os.environ.get('FAULTS') and '扮演白展堂进行对话' in first:
                return self._json(200, {'id': 'x', 'object': 'chat.completion', 'model': 'mock-model', 'choices': [{'index': 0, 'message': {'role': 'assistant', 'content': ''}, 'finish_reason': 'stop'}], 'usage': {'prompt_tokens': 1, 'completion_tokens': 0, 'total_tokens': 1}})
            h = int(hashlib.sha256(last.encode()).hexdigest(), 16)
            # Some replies have a second line, to exercise the first-line rule.
            text = f'模拟回复{h % 1000}：（点头）好的。' + ('\n第二行不应计分' if h % 3 == 0 else '')
            return self._json(200, {
                'id': 'x', 'object': 'chat.completion', 'model': 'mock-model',
                'choices': [{'index': 0, 'message': {'role': 'assistant', 'content': text}, 'finish_reason': 'stop'}],
                'usage': {'prompt_tokens': len(json.dumps(msgs)) // 4, 'completion_tokens': 20, 'total_tokens': 0},
            })
        self._json(404, {'error': {'message': 'not found'}})


def _h(t):
    return int(hashlib.sha256(t.encode()).hexdigest(), 16)


def _reply(self, req, text):
    self._json(200, {
        'id': 'x', 'object': 'chat.completion', 'model': req.get('model', 'mock'),
        'choices': [{'index': 0, 'message': {'role': 'assistant', 'content': text}, 'finish_reason': 'stop'}],
        'usage': {'prompt_tokens': len(json.dumps(req.get('messages', []))) // 4, 'completion_tokens': len(text) // 4, 'total_tokens': 0, 'cost': 0.0},
    })


DIMS = ['Storyline Consistency', 'Anthropomorphism', 'Character Fidelity', 'Storyline Quality']


def coser(self, req):
    """CoSER calls: the critic, its JSON fix, the next-speaker predictor, the environment and the characters."""
    msgs = req.get('messages', [])
    if not msgs:
        return False
    first, last = msgs[0]['content'], msgs[-1]['content']
    h = _h(json.dumps(msgs, ensure_ascii=False))
    if first.startswith('I will provide you with a JSON string'):
        _reply(self, req, json.dumps({'Storyline Quality': {'flaws': [{'instance': 'fixed', 'type': 'Flow & Progression', 'severity': 2}]}, 'Character Fidelity': {'flaws': []}, 'Storyline Consistency': {'flaws': []}, 'Anthropomorphism': {'flaws': [{'instance': 'fixed', 'type': 'Self-identity', 'severity': 1}]}}))
        return True
    if 'You are a literary critic' in first:
        dim = next(d for d in DIMS if f'in terms of {d},' in first)
        if h % 5 == 0:
            return _reply(self, req, 'I cannot produce JSON right now, sorry.') or True
        n = h % 4
        flaws = [{'instance': f'flaw {i} in {dim}', 'type': dim, 'severity': (h >> (i * 3)) % 5 + 1} for i in range(n)]
        if h % 7 == 0 and flaws:
            flaws[0]['severity'] = 3.0  # a float literal: Python does not count it
        if h % 11 == 0 and flaws:
            del flaws[-1]['severity']  # missing: counted as 1
        _reply(self, req, 'Here is my evaluation:\n```json\n' + json.dumps({dim: {'flaws': flaws}}, indent=2) + '\n```')
        return True
    if first.startswith('Your task is to predict the next speaker'):
        names = json.loads(first.split('Choose a name from this list: ')[1].split('. If it')[0].replace("'", '"')) if "'" in first else []
        k = h % 10
        if k == 0:
            out = '1. Reasoning: unclear.\n2. Next Speaker: random'
        elif k == 1:
            out = '1. Reasoning: the scene is over.\n2. Next Speaker: <END CHAT>'
        elif k == 2:
            out = '**Next Speaker:** ' + (names[0] if names else 'Environment')
        else:
            out = '1. Reasoning: it is their turn.\n2. Next Speaker: ' + (names[h % len(names)] if names else 'Environment')
        _reply(self, req, out)
        return True
    if 'Your task is to provide the environmental feedback' in first:
        _reply(self, req, f'(A gust of wind rattles the shutters; somewhere a dog barks {h % 7} times.)')
        return True
    if first.startswith('You are ') and ' from ' in first.split('\n')[0]:
        name = first.split('\n')[0][len('You are '):].split(' from ')[0]
        k = h % 6
        if k == 0:
            out = f'{name}: [I must stay calm.] (looks away) I have nothing more to say about it.'
        elif k == 1:
            out = f'[This is unbearable.] (clenches fists) You never listen to me, do you? You never listen to me, do you? You never listen to me, do you? You never listen to me, do you? You never listen to me, do you?'
        else:
            out = f'[{name} weighs the words.] (nods slowly) Very well. Line {h % 97}.'
        _reply(self, req, out)
        return True
    return False


H.coser = coser
ThreadingHTTPServer(('127.0.0.1', int(sys.argv[1])), H).serve_forever()
