const e=require("./registration"),t=require("b4a"),{isMemberActive:a,getMemberForDeviceKey:l}=require("./query"),{KeetContractError:r}=require("./errors"),{addEvent:s}=require("./chat"),{contract:{hyperschema:{getEnum:i}}}=require("../../../schema/spec"),{trackActivity:c}=require("./hooks"),y=i("@keet/room-type");function d(e){return e.stats.calls||(e.stats.calls={streams:0}),e.stats.calls.streams+=1,e.stats.calls.streams}async function m(e,{db:t,writer:i}){const c=i.key,[y,m,o,n,K,w]=await Promise.all([l(t,c),t.get("@keet/calls",{callKey:e.callKey}),t.get("@keet/devices",{key:c}),t.findOne("@keet/active-calls-by-timestamp"),t.get("@keet/system-digest"),t.get("@keet/threads",{id:0})]);if(!a(y))throw r.INACTIVE_MEMBER();if(m)throw r.CALL_KEY_EXISTS();if(n)throw r.ACTIVE_CALL_EXISTS();if(K.settings.isCallingDisabled)throw r.CALL_DISABLED();if(!o)throw r.ABORT();if(await t.insert("@keet/calls",{version:e.version,callKey:e.callKey,encryptionKey:e.encryptionKey,discoveryKey:e.discoveryKey,createdAt:e.header.timestamp}),await t.insert("@keet/call-members",{callKey:e.callKey,memberKey:y.key,createdAt:e.header.timestamp}),await t.insert("@keet/call-devices",{callKey:e.callKey,deviceKey:o.key,swarmKey:3===e.version?e.swarmKey:o.profile.swarmKey,memberKey:y.key,createdAt:e.header.timestamp,lastHeartbeatAt:e.header.timestamp}),3===e.version&&e.streamInputs?.length){for(const{streamKey:a,mediaStreamId:l,type:r,muted:s}of e.streamInputs){const i=d(w);await t.insert("@keet/call-streams",{callKey:e.callKey,deviceKey:o.key,streamKey:a,statusKey:i,mediaStreamId:l,type:r,createdAt:e.header.timestamp}),await t.insert("@keet/call-stream-statuses",{statusKey:i,muted:s})}await t.insert("@keet/threads",w)}return await s(t,i,{callStarted:!0},e.header.timestamp),{writer:i,writerMember:y}}async function o(e,{db:t,writer:s}){const i=e.callKey,c=s.key,[y,m,o,n]=await Promise.all([l(t,c),t.get("@keet/calls",{callKey:i}),t.get("@keet/devices",{key:c}),t.get("@keet/threads",{id:0})]);if(!a(y))throw r.INACTIVE_MEMBER();if(!m)throw r.CALL_NOT_FOUND();if(m.endedAt)throw r.CALL_ENDED();if(!o)throw r.ABORT();const[K,w]=await Promise.all([t.get("@keet/call-members",{callKey:e.callKey,memberKey:y.key}),t.get("@keet/call-devices",{callKey:e.callKey,deviceKey:c})]);if(w)throw r.CALL_DEVICE_EXISTS();if(await t.insert("@keet/call-devices",{callKey:e.callKey,deviceKey:o.key,swarmKey:3===m.version?e.swarmKey:o.profile.swarmKey,memberKey:y.key,createdAt:e.header.timestamp,lastHeartbeatAt:e.header.timestamp}),3===m.version&&e.streamInputs?.length){for(const{streamKey:a,mediaStreamId:l,type:r,muted:s}of e.streamInputs){const i=d(n);await t.insert("@keet/call-streams",{callKey:e.callKey,deviceKey:o.key,streamKey:a,statusKey:i,mediaStreamId:l,type:r,createdAt:e.header.timestamp}),await t.insert("@keet/call-stream-statuses",{statusKey:i,muted:s})}await t.insert("@keet/threads",n)}return K||await t.insert("@keet/call-members",{callKey:e.callKey,memberKey:y.key,createdAt:e.header.timestamp}),{writer:s,writerMember:y}}async function n({header:e,callKey:t},{db:s,writer:i}){const c=i.key,d={gte:{callKey:t},lte:{callKey:t}},[m,o,n,K]=await Promise.all([l(s,c),s.get("@keet/calls",{callKey:t}),s.find("@keet/call-members",d).toArray(),s.get("@keet/system-digest")]);if(K.settings.roomType===y.DirectMessage)throw r.CALL_LEAVE_NOT_ALLOWED();if(!a(m))throw r.INACTIVE_MEMBER();if(!o)throw r.CALL_NOT_FOUND();const w=m.key,k={gte:{callKey:t,memberKey:w},lte:{callKey:t,memberKey:w}},h={gte:{callKey:t,deviceKey:c},lte:{callKey:t,deviceKey:c}},[A,_,E,f]=await Promise.all([s.get("@keet/call-members",{callKey:t,memberKey:w}),s.get("@keet/call-devices",{callKey:t,deviceKey:c}),s.find("@keet/call-devices-by-member-key",k).toArray(),s.find("@keet/call-streams-by-device-key",h).toArray()]);if(!A)throw r.CALL_MEMBER_NOT_FOUND();if(!_)throw r.CALL_DEVICE_NOT_FOUND();const L=[];L.push(s.delete("@keet/call-devices",_));for(const e of f)L.push(s.delete("@keet/call-streams",e)),L.push(s.delete("@keet/call-stream-statuses",{statusKey:e.statusKey}));return E.length>1?(await Promise.all(L),{writer:i,writerMember:m}):(L.push(s.delete("@keet/call-members",A)),1===n.length&&(o.endedAt=e.timestamp,L.push(s.insert("@keet/calls",o))),await Promise.all(L),{writer:i,writerMember:m})}async function K({header:e,callKey:t},{db:i,writer:c}){const d=c.key,[m,o,n,K]=await Promise.all([l(i,d),i.get("@keet/calls",{callKey:t}),i.get("@keet/call-devices",{callKey:t,deviceKey:d}),i.get("@keet/system-digest")]);if(!a(m))throw r.INACTIVE_MEMBER();if(!o)throw r.CALL_NOT_FOUND();if(o.endedAt)throw r.CALL_ENDED();const w=K.settings.roomType===y.DirectMessage;if(!w&&!n)throw r.CALL_DEVICE_NOT_FOUND();o.endedAt=e.timestamp,await i.insert("@keet/calls",o);const k={gte:{callKey:t},lte:{callKey:t}},[h,A,_,E]=await Promise.all([i.get("@keet/call-members",{callKey:t,memberKey:m.key}),i.find("@keet/call-members",k).toArray(),i.find("@keet/call-devices",k).toArray(),i.find("@keet/call-streams",k).toArray()]),f=[];for(const e of A)f.push(i.delete("@keet/call-members",e));for(const e of _)f.push(i.delete("@keet/call-devices",e));for(const e of E)f.push(i.delete("@keet/call-streams",e)),f.push(i.delete("@keet/call-stream-statuses",{statusKey:e.statusKey}));return await Promise.all(f),w&&(A.length>1||!h?await s(i,c,{callHangup:!0},e.timestamp):await s(i,c,{callMissed:!0},e.timestamp)),{writer:c,writerMember:m}}async function w({header:e,callKey:t,streamKey:s,mediaStreamId:i,type:c,muted:y},{db:m,writer:o}){const n=o.key,K={gte:{callKey:t,mediaStreamId:i},lte:{callKey:t,mediaStreamId:i}},[w,k,h,A,_,E]=await Promise.all([l(m,n),m.get("@keet/calls",{callKey:t}),m.get("@keet/call-devices",{callKey:t,deviceKey:n}),m.get("@keet/call-streams",{callKey:t,streamKey:s}),i?m.findOne("@keet/call-streams-by-media-id",K):null,m.get("@keet/threads",{id:0})]);if(!a(w))throw r.INACTIVE_MEMBER();if(!k)throw r.CALL_NOT_FOUND();if(k.endedAt)throw r.CALL_ENDED();if(!h)throw r.CALL_DEVICE_NOT_FOUND();if(A)throw r.CALL_STREAM_EXISTS();if(_)throw r.CALL_MEDIA_STREAM_ID_EXISTS();const f=d(E),L={callKey:t,deviceKey:h.deviceKey,streamKey:s,mediaStreamId:i,type:c,statusKey:f,createdAt:e.timestamp};return await Promise.all([m.insert("@keet/call-streams",L),m.insert("@keet/call-stream-statuses",{statusKey:f,muted:y}),m.insert("@keet/threads",E)]),{writer:o,writerMember:w}}async function k({callKey:e,streamKey:s,muted:i},{db:c,writer:y}){const d=y.key,[m,o,n,K]=await Promise.all([l(c,d),c.get("@keet/calls",{callKey:e}),c.get("@keet/call-devices",{callKey:e,deviceKey:d}),c.get("@keet/call-streams",{callKey:e,streamKey:s})]);if(!a(m))throw r.INACTIVE_MEMBER();if(!o)throw r.CALL_NOT_FOUND();if(o.endedAt)throw r.CALL_ENDED();if(!n)throw r.CALL_DEVICE_NOT_FOUND();if(!K)throw r.CALL_STREAM_NOT_FOUND();if(!t.equals(K.deviceKey,d))throw r.CALL_STREAM_NOT_OWNED();return await c.insert("@keet/call-stream-statuses",{statusKey:K.statusKey,muted:i}),{writer:y,writerMember:m}}async function h({callKey:e,streamKey:t},{db:s,writer:i}){const c=i.key,[y,d,m,o]=await Promise.all([l(s,c),s.get("@keet/calls",{callKey:e}),s.get("@keet/call-devices",{callKey:e,deviceKey:c}),s.get("@keet/call-streams",{callKey:e,streamKey:t})]);if(!a(y))throw r.INACTIVE_MEMBER();if(!d)throw r.CALL_NOT_FOUND();if(d.endedAt)throw r.CALL_ENDED();if(!m)throw r.CALL_DEVICE_NOT_FOUND();if(!o)throw r.CALL_STREAM_NOT_FOUND();return await Promise.all([s.delete("@keet/call-streams",o),s.delete("@keet/call-stream-statuses",{statusKey:o.statusKey})]),{writer:i,writerMember:y}}async function A({header:e,callKey:t,deviceKey:s},{db:i,writer:c}){const y=c.key,d={gte:{callKey:t},lte:{callKey:t}},[m,o,n,K]=await Promise.all([l(i,y),i.get("@keet/calls",{callKey:t}),i.get("@keet/call-devices",{callKey:t,deviceKey:s}),i.find("@keet/call-members",d).toArray()]);if(!a(m))throw r.INACTIVE_MEMBER();if(!o)throw r.CALL_NOT_FOUND();if(o.endedAt)throw r.CALL_ENDED();if(!n)throw r.CALL_DEVICE_NOT_FOUND();const w=n.memberKey,k={gte:{callKey:t,memberKey:w},lte:{callKey:t,memberKey:w}},h={gte:{callKey:t,deviceKey:s},lte:{callKey:t,deviceKey:s}},[A,_,E]=await Promise.all([i.get("@keet/call-members",{callKey:t,memberKey:w}),i.find("@keet/call-devices-by-member-key",k).toArray(),i.find("@keet/call-streams-by-device-key",h).toArray()]),f=[];f.push(i.delete("@keet/call-devices",n));for(const e of E)f.push(i.delete("@keet/call-streams",e)),f.push(i.delete("@keet/call-stream-statuses",{statusKey:e.statusKey}));return _.length>1||(A&&f.push(i.delete("@keet/call-members",A)),1===K.length&&(o.endedAt=e.timestamp,f.push(i.insert("@keet/calls",o)))),Promise.all(f)}async function _({header:e,callKey:t},{db:s,writer:i}){const c=i.key,[y,d,m]=await Promise.all([l(s,c),s.get("@keet/calls",{callKey:t}),s.get("@keet/call-devices",{callKey:t,deviceKey:c})]);if(!a(y))throw r.INACTIVE_MEMBER();if(!d)throw r.CALL_NOT_FOUND();if(d.endedAt)throw r.CALL_ENDED();if(!m)throw r.CALL_DEVICE_NOT_FOUND();await s.insert("@keet/call-devices",{...m,lastHeartbeatAt:e.timestamp})}async function E({callKey:e},{db:t,writer:s}){const i=s.key,[c,y,d]=await Promise.all([l(t,i),t.get("@keet/calls",{callKey:e}),t.get("@keet/call-devices",{callKey:e,deviceKey:i})]);if(!a(c))throw r.INACTIVE_MEMBER();if(!y)throw r.CALL_NOT_FOUND();if(y.endedAt)throw r.CALL_ENDED();if(!d)throw r.CALL_DEVICE_NOT_FOUND();if(d.isRecording)throw r.CALL_RECORDING_ALREADY_ACTIVE();await t.insert("@keet/call-devices",{...d,isRecording:!0})}async function f({callKey:e},{db:t,writer:s}){const i=s.key,[c,y,d]=await Promise.all([l(t,i),t.get("@keet/calls",{callKey:e}),t.get("@keet/call-devices",{callKey:e,deviceKey:i})]);if(!a(c))throw r.INACTIVE_MEMBER();if(!y)throw r.CALL_NOT_FOUND();if(y.endedAt)throw r.CALL_ENDED();if(!d)throw r.CALL_DEVICE_NOT_FOUND();if(!d.isRecording)throw r.CALL_NOT_RECORDING();await t.insert("@keet/call-devices",{...d,isRecording:!1})}module.exports={[e]:function(e){e.add("@keet/start-call",c(m)),e.add("@keet/join-call",c(o)),e.add("@keet/leave-call",c(n)),e.add("@keet/end-call",c(K)),e.add("@keet/add-call-stream",c(w)),e.add("@keet/remove-call-stream",c(h)),e.add("@keet/update-call-stream",c(k)),e.add("@keet/call-device-heartbeat",c(_)),e.add("@keet/remove-call-device",c(A)),e.add("@keet/start-call-recording",c(E)),e.add("@keet/stop-call-recording",c(f))}};