#!/usr/bin/env python3
"""Synthetic intake reliability reference: no customer data, no external writes."""
from __future__ import annotations
import hashlib, hmac, json, sqlite3, time
from pathlib import Path

MAX_BYTES = 8192
ALLOWED_EVENTS = {"booking.created", "booking.changed", "booking.cancelled"}

def sign(secret: bytes, raw: bytes) -> str:
    return "sha256=" + hmac.new(secret, raw, hashlib.sha256).hexdigest()

class Intake:
    def __init__(self, db: str, secret: bytes, tenants: set[str]):
        self.db, self.secret, self.tenants = db, secret, tenants
        with self.conn() as c:
            c.execute("""CREATE TABLE IF NOT EXISTS events(
                 tenant TEXT NOT NULL, event_id TEXT NOT NULL,
                 kind TEXT NOT NULL, payload_json TEXT NOT NULL,
                 state TEXT NOT NULL DEFAULT 'queued',
                 attempts INTEGER NOT NULL DEFAULT 0,
                 due_epoch INTEGER NOT NULL,
                 created_epoch INTEGER NOT NULL,
                 PRIMARY KEY(tenant,event_id))""")
            c.execute("CREATE INDEX IF NOT EXISTS due_events ON events(state,due_epoch)")
    def conn(self):
        c = sqlite3.connect(self.db,timeout=15,isolation_level=None)
        c.execute("PRAGMA busy_timeout=15000")
        c.execute("PRAGMA journal_mode=WAL")
        return c
    def ingest(self, raw: bytes, signature: str):
        if len(raw) > MAX_BYTES: return 413, {"error":"too_large"}
        if not hmac.compare_digest(signature,sign(self.secret,raw)):
            return 401,{"error":"invalid_signature"}
        try:
            p=json.loads(raw)
            if not isinstance(p,dict): raise ValueError()
            tenant,eid,kind=p["restaurant_id"],p["unique_event_id"],p["type"]
            if not isinstance(tenant,str) or tenant not in self.tenants:
                return 403,{"error":"unknown_tenant"}
            if not isinstance(eid,str) or not 1<=len(eid)<=128:
                return 422,{"error":"bad_event_id"}
            if kind not in ALLOWED_EVENTS:
                return 422,{"error":"unsupported_event"}
            if not isinstance(p.get("data"),dict):
                return 422,{"error":"bad_data"}
        except (ValueError,TypeError,KeyError):
            return 422,{"error":"bad_payload"}
        now=int(time.time())
        with self.conn() as c:
            cur=c.execute("""INSERT OR IGNORE INTO events
                (tenant,event_id,kind,payload_json,due_epoch,created_epoch)
                VALUES (?,?,?,?,?,?)""",
                (tenant,eid,kind,json.dumps(p["data"],separators=(',',':')),now,now))
            inserted=cur.rowcount==1
            row=c.execute("SELECT state FROM events WHERE tenant=? AND event_id=?",
                (tenant,eid)).fetchone()
        return (202 if inserted else 200),{
            "accepted": True, "duplicate":not inserted, "state":row[0],
            "event_id":eid,"restaurant_id":tenant}
    def claim(self):
        now=int(time.time())
        with self.conn() as c:
            c.execute("BEGIN IMMEDIATE")
            row=c.execute("""SELECT tenant,event_id,kind,payload_json,attempts
                  FROM events WHERE state IN ('queued','retry') AND due_epoch<=?
                  ORDER BY created_epoch ASC LIMIT 1""",(now,)).fetchone()
            if row:
                c.execute("""UPDATE events SET state='processing',attempts=attempts+1
                    WHERE tenant=? AND event_id=?""",row[:2])
            c.commit()
        return row
    def complete(self,tenant:str,eid:str,success:bool):
        with self.conn() as c:
            cur=c.execute("SELECT attempts,state FROM events WHERE tenant=? AND event_id=?",
                          (tenant,eid)).fetchone()
            if not cur or cur[1]!="processing": return False
            attempts=cur[0]
            if success: state,due="done",int(time.time())
            elif attempts>=5: state,due="dead_letter",int(time.time())
            else: state,due="retry",int(time.time())+min(300,2**attempts)
            c.execute("UPDATE events SET state=?,due_epoch=? WHERE tenant=? AND event_id=?",
                      (state,due,tenant,eid))
        return True

def sample(eid="evt-1",tenant="restaurant-demo-1",kind="booking.created"):
    return json.dumps({"restaurant_id":tenant,"unique_event_id":eid,"type":kind,
            "data":{"booking_time":"2026-10-15T13:30:00Z","guest_count":2}},
            separators=(',',':')).encode()
