"""Public teaching fixture: loopback-only, generated tone, no user files or uploads.
Run with Python 3.9+. Prints two URLs; Ctrl+C stops both servers.
"""
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlsplit
from threading import Thread, Event
import io, wave, struct, math, json, re

def fixture_wave():
    data=b''.join(struct.pack('<h',round(3000*math.sin(2*math.pi*440*n/16000))) for n in range(32000))
    output=io.BytesIO()
    with wave.open(output,'wb') as f:
        f.setnchannels(1);f.setsampwidth(2);f.setframerate(16000);f.writeframes(data)
    return output.getvalue()

WAV=fixture_wave()
VTT='''WEBVTT

intro
00:00.000 --> 00:00.500
آغازِ تمرین

middle
00:00.500 --> 00:01.250
بخشِ میانی

ending
00:01.250 --> 00:02.000
پایانِ تمرین
'''.encode('utf-8')
ORIGIN_A=''

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        p=urlsplit(self.path).path
        status=200;extra={}
        if p=='/':body=b'<!doctype html><html lang="en"><meta charset="utf-8"><title>Local synthetic audio fixture</title><body><h1>Local generated tone: no speech or user data</h1></body></html>';mime='text/html; charset=utf-8'
        elif p=='/tone.wav':body=WAV;mime='audio/wav'
        elif p=='/tone-generic':body=WAV;mime='application/octet-stream'
        elif p=='/fake.wav':body=b'<html>not audio bytes</html>';mime='audio/wav'
        elif p in ('/chapters.vtt','/allowed.vtt','/blocked.vtt'):
            body=VTT;mime='text/vtt; charset=utf-8'
            if p=='/allowed.vtt':extra['Access-Control-Allow-Origin']=ORIGIN_A;extra['Vary']='Origin'
        else:status=404;body=b'not found';mime='text/plain; charset=utf-8'
        if p in ('/tone.wav','/tone-generic'):
            extra['Accept-Ranges']='bytes'
            requested=self.headers.get('Range')
            if requested:
                match=re.fullmatch(r'bytes=(\d+)-(\d*)',requested)
                size=len(body)
                if match:
                    start=int(match[1]);end=min(int(match[2]) if match[2] else size-1,size-1)
                if not match or start>=size or end<start:
                    status=416;body=b'';extra['Content-Range']='bytes */'+str(size)
                else:
                    status=206;extra['Content-Range']=f'bytes {start}-{end}/{size}';body=body[start:end+1]
        self.send_response(status);self.send_header('Content-Type',mime);self.send_header('Content-Length',str(len(body)));self.send_header('Cache-Control','no-store')
        for k,v in extra.items():self.send_header(k,v)
        self.end_headers();self.wfile.write(body)
    def log_message(self,*args):pass

if __name__=='__main__':
    servers=[ThreadingHTTPServer(('127.0.0.1',0),Handler) for _ in range(2)]
    ORIGIN_A='http://127.0.0.1:'+str(servers[0].server_port)
    origins=[ORIGIN_A,'http://127.0.0.1:'+str(servers[1].server_port)]
    for s in servers:Thread(target=s.serve_forever,daemon=True).start()
    print(json.dumps({'origins':origins,'wave_seconds':2,'real_speech':False,'public_network_listener':False}),flush=True)
    try:Event().wait()
    except KeyboardInterrupt:pass
    finally:
        for s in servers:s.shutdown();s.server_close()
