"""Authenticated InFinBot owner gateway. No order, payment or license activation API."""
import argparse
import asyncio
import hmac
import json
import logging
from pathlib import Path
import ssl
from urllib.parse import urlsplit
from infinbot_common import private_read,strict_json,Rejected,readonly_db,timestamp
from risk_state import RiskState


class ConsigliereWebSocketServer:
    def __init__(self,*,token_file,risk_state,db_filepath,host='127.0.0.1',port=8765,origin='http://127.0.0.1:8766',tls_context=None):
        self.token_file=Path(token_file);self.risk=RiskState(risk_state);self.db_filepath=str(db_filepath)
        self.host,self.port,self.origin,self.tls_context=host,port,origin,tls_context
        if host not in ('127.0.0.1','::1') and (tls_context is None or urlsplit(origin).scheme!='https'):
            raise Rejected('Remote gateway requires HTTPS origin and TLS certificate/key')
        self.token();self.connected_clients=set()

    def token(self):
        value=private_read(self.token_file,1024).decode().strip()
        if len(value)<43:raise Rejected('Owner-private random gateway token required')
        return value

    def health(self):
        result={'product':'InFinBot','observed_at':timestamp(),'risk':self.risk.status(),
                'ledger_status':'UNVERIFIED','broker_orders_submitted':0,'live_clearance_granted':False}
        try:
            with readonly_db(self.db_filepath) as c:
                result['ledger_status']='READ_ONLY_INTEGRITY_OK'
                result['table_count']=c.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='table'").fetchone()[0]
        except Exception:result['ledger_status']='UNAVAILABLE'
        return result

    def command(self,payload):
        if not isinstance(payload,dict) or set(payload)!={'action'}:return {'status':'COMMAND_REJECTED','reason':'Unsupported command shape'}
        if payload['action']=='health':return {'status':'READ_COMPLETED','result':self.health()}
        if payload['action']=='freeze_entries':return {'status':'ENTRY_FREEZE_PERSISTED','result':self.risk.freeze('AUTHENTICATED_OWNER_FREEZE'),'broker_liquidation_verified':False}
        return {'status':'COMMAND_REJECTED_UNSUPPORTED','broker_orders_submitted':0}

    async def register_client_handshake(self,websocket):
        try:
            raw=await asyncio.wait_for(websocket.recv(),timeout=5)
            payload=strict_json(raw)
            if (not isinstance(payload,dict) or set(payload)!={'action','token'} or payload['action']!='authenticate' or
                not isinstance(payload['token'],str) or not hmac.compare_digest(payload['token'],self.token())):
                await websocket.close(1008,'Authentication required');return
            original=payload['token'];self.connected_clients.add(websocket)
            await websocket.send(json.dumps({'status':'OWNER_AUTHENTICATED','product':'InFinBot','orders_enabled':False}))
            async for raw in websocket:
                if not hmac.compare_digest(original,self.token()):await websocket.close(1008,'Session revoked');return
                try:result=self.command(strict_json(raw))
                except (ValueError,OSError):result={'status':'COMMAND_REJECTED'}
                await websocket.send(json.dumps(result,allow_nan=False))
        except Exception:
            try:await websocket.close(1008,'Session unavailable')
            except Exception:pass
        finally:self.connected_clients.discard(websocket)

    async def serve(self):
        from websockets.asyncio.server import serve
        logger=logging.getLogger('infinbot.gateway');logger.setLevel(logging.WARNING)
        async with serve(self.register_client_handshake,self.host,self.port,origins=[self.origin,None],
                         ssl=self.tls_context,max_size=4096,max_queue=4,ping_interval=15,ping_timeout=10,
                         close_timeout=2,compression=None,logger=logger):
            await asyncio.Future()


def main():
    p=argparse.ArgumentParser(description=__doc__)
    for flag in ('token-file','state-path','db-path'):p.add_argument('--'+flag,required=True)
    p.add_argument('--host',default='127.0.0.1');p.add_argument('--port',type=int,default=8765)
    p.add_argument('--origin',default='http://127.0.0.1:8766');p.add_argument('--tls-cert');p.add_argument('--tls-key')
    args=p.parse_args();context=None
    try:
        if args.tls_cert or args.tls_key:
            if not (args.tls_cert and args.tls_key):raise Rejected('Both certificate and key required')
            private_read(args.tls_key,65536)
            context=ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER);context.minimum_version=ssl.TLSVersion.TLSv1_2
            context.load_cert_chain(args.tls_cert,args.tls_key)
        app=ConsigliereWebSocketServer(token_file=args.token_file,risk_state=args.state_path,db_filepath=args.db_path,
                                      host=args.host,port=args.port,origin=args.origin,tls_context=context)
        asyncio.run(app.serve())
    except KeyboardInterrupt:return 0
    except Exception:
        print(json.dumps({'status':'GATEWAY_BLOCKED','reason':'Check private configuration, dependencies and TLS requirements'}));return 1


if __name__=='__main__':raise SystemExit(main())
