const e=require("@holepunchto/keet-registry-client"),t=require("ready-resource"),s=require("@holepunchto/keet-registry"),i=require("b4a"),{createUsernameReservationRequest:r}=require("../invitations"),{Readable:n}=require("streamx"),a=require("@holepunchto/keet-registry/lib/errors");function o(e){return{hasRecord:!0===e.hasRecord,hasError:!0===e.hasError,isReserving:!0===e.isReserving,isReserved:!0===e.isReserved||!0===e.hasRecord}}module.exports=class extends t{constructor(e,{db:t,reserved:s,blobs:i,rpc:r,rateLimit:n={}}={}){super(),this.dbKey=t,this.reservedKey=s,this.blobsKey=i,this.rpcKeys=r,this.keet=e,this.store=null,this.swarm=null,this.swarmWorker=null,this.db=null,this.reserved=null,this.blobs=null,this._keetRegistry=null,this._rpcClient=null,this._registryRpcOpts={rateLimit:n},this._subs=[],this._watchingReservation=!1,this._watchingRecord=!1,this._boundEmit=this._emit.bind(this),this._boundCatch=this._catch.bind(this),this._boundLookupOwn=null}async _open(){this.store=this.keet.store.session(),this.swarmWorker=this.keet.swarmWorker,this.db=this.store.get({key:this.dbKey}),this.reserved=this.store.get({key:this.reservedKey}),this.blobs=this.store.get({key:this.blobsKey}),this._keetRegistry=new s(this.db,this.reserved,this.blobs,{extension:!1}),this._rpcClient=new e(this.rpcKeys,this.keet.rpcClient,this._registryRpcOpts),await this._keetRegistry.ready();const t=await this._getIdentity();this._boundLookupOwn=this._safeLookupOwn.bind(this,t),this._lookupOwn(t).catch(this.keet.onerror),this._keetRegistry.reserved.core.once("append",this._boundLookupOwn)}_emit(e,t=null,s=null){for(const i of this._subs){if(this.keet.closing)return;i.push({status:o(e),reservation:t,record:s})}}async _catch(e){this._unwatch();for(const t of this._subs)t.push({status:o({hasError:!0}),error:e.code??e.message})}async _close(){this._unwatch(),this._rpcClient.destroy();try{await this._keetRegistry.close()}catch(e){if("REQUEST_CANCELLED"!==e.code)throw e}await this.store.close()}async _getIdentity(){const e=await this.keet.local.get("@local/identity");if(!e)throw new Error("Identity is required");return e}_encode(e){return e?{...e,contactInfo:this.keet.internalEncodeContactInfo(e)}:null}async _safeLookupOwn(e){try{return await this._lookupOwn(e)}catch(e){this.keet.onerror(e)}}async _lookupOwn(e){const[t,s]=await Promise.all([this._keetRegistry.getByKey(e.publicKey),this._keetRegistry.getReservationsByKey(e.publicKey)]);if(!t){if(s.length&&!this._watchingRecord){const e=s.pop();this._boundEmit({isReserved:!0},{username:e.username}),this._claimReservation(e)}return null}return this._encode(t)}async lookup(e){if(!e)throw new Error("Username required");this.opened||await this.ready(),this.swarmWorker.startDiscoverySwarming(this.db.discoveryKey,{deferSwarming:!1,limit:1});const t=await this._keetRegistry.get(e);return this._encode(t)}async lookupByKey(e){if(!e)throw new Error("Public key required");this.opened||await this.ready(),this.swarmWorker.startDiscoverySwarming(this.db.discoveryKey,{deferSwarming:!1,limit:1});const t=await this._getIdentity();if(i.equals(t.publicKey,e))return this._lookupOwn(t);const s=await this._keetRegistry.getByKey(e);return this._encode(s)}async search(e,t){if(!e)return[];this.opened||await this.ready(),this.swarmWorker.startDiscoverySwarming(this.db.discoveryKey,{deferSwarming:!1,limit:1});const s=[];for await(const i of this._keetRegistry.search(e,t))s.push(this._encode(i));return s}async isAvailable(e){if(!e)throw new Error("Username required");return this.opened||await this.ready(),this.swarmWorker.startDiscoverySwarming(this.db.discoveryKey,{deferSwarming:!1,limit:1}),this._keetRegistry.isAvailable(e)}async getReservationsByKey(e){if(!e)throw new Error("Public key required");return this.opened||await this.ready(),this.swarmWorker.startDiscoverySwarming(this.db.discoveryKey,{deferSwarming:!1,limit:1}),this._keetRegistry.getReservationsByKey(e)}async internalUpdateRegistryMember(e){try{await s.verifyPut(this._keetRegistry,e),await this._rpcClient.putRecord(e)}catch(e){this._boundCatch(e)}}async _pushOwnRecord(){const{publicKey:e}=await this._getIdentity(),t=await this._keetRegistry.getByKey(e);t&&this._boundEmit({hasRecord:!0},null,t)}subscribeUsername(){const e=this;return new n({open(t){e._subs.push(this),e._pushOwnRecord().catch(e.keet.onerror),t(null)},destroy(t){const s=e._subs.indexOf(this);-1!==s&&e._subs.splice(s,1),t(null)}})}async update(e){if(!e)throw new Error("Username required");if(this.opened||await this.ready(),this._watchingReservation)throw new Error("Registration already in progress");const{publicKey:t,deviceKeyPair:i,deviceProof:n}=await this._getIdentity();this.swarmWorker.startDiscoverySwarming(this.db.discoveryKey,{deferSwarming:!1,limit:1}),this._watchReservation(e,t);try{this._boundEmit({isReserving:!0},{username:e});const a=r(e,{publicKey:t,deviceKeyPair:i,deviceProof:n});await s.verifyReservation(a),await this._rpcClient.reserve(a)}catch(e){return this._boundCatch(e),!1}return!0}_unwatch(){this._keetRegistry.unwatch(),this._watchingRecord=!1,this._watchingReservation=!1}async _claimReservation({username:e,memberKey:t}){this._keetRegistry.reserved.core.removeListener("append",this._boundLookupOwn),this._boundEmit({isReserved:!0},{username:e,memberKey:t}),this._watchRecord(e,t),await this.keet.internalUpdateProfile({username:e},{forceUpdateRegistry:!0}).catch(this._boundCatch)}_watchReservation(e,t){const s=this;this._watchingReservation=!0,this._keetRegistry.watchReservation(e).then(async function(e){s._watchingReservation=!1,null!==e&&(i.equals(e.memberKey,t)?await s._claimReservation(e):s._boundCatch(a.NAME_UNAVAILABLE()))},this._boundCatch)}_watchRecord(e,t){const s=this,i={attestation:{memberKey:t}};this._watchingRecord=!0,this._keetRegistry.watchRecord(i,function(t){return t.profile.username===e}).then(function(e){s._watchingRecord=!1,null!==e&&s._boundEmit({hasRecord:!0},null,e)},this._boundCatch)}};