var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { isPortableDid } from '@enbox/dids';
import { BearerIdentity } from './bearer-identity.js';
import { getDwnServiceEndpointUrls } from './utils.js';
import { InMemoryIdentityStore } from './store-identity.js';
export function isPortableIdentity(obj) {
    // Validate that the given value is an object that has the necessary properties of PortableIdentity.
    return !(!obj || typeof obj !== 'object' || obj === null)
        && 'did' in obj
        && 'metadata' in obj
        && isPortableDid(obj.did);
}
/**
 * This API is used to manage and interact with Identities within the Web5 Agent framework.
 * An Identity is a DID that is associated with metadata that describes the Identity.
 * Metadata includes A name(label), and whether or not the Identity is connected (delegated to act on the behalf of another DID).
 *
 * A KeyManager is used to manage the cryptographic keys associated with the Identities.
 *
 * The `DidApi` is used internally to create, store, and manage DIDs.
 * When a DWN Data Store is used, the Identity and DID information are stored under the Agent DID's tenant.
 */
export class AgentIdentityApi {
    constructor({ agent, store } = {}) {
        this._agent = agent;
        // If `store` is not given, use an in-memory store by default.
        this._store = store !== null && store !== void 0 ? store : new InMemoryIdentityStore();
    }
    /**
     * Retrieves the `Web5PlatformAgent` execution context.
     *
     * @returns The `Web5PlatformAgent` instance that represents the current execution context.
     * @throws Will throw an error if the `agent` instance property is undefined.
     */
    get agent() {
        if (this._agent === undefined) {
            throw new Error('AgentIdentityApi: Unable to determine agent execution context.');
        }
        return this._agent;
    }
    set agent(agent) {
        this._agent = agent;
    }
    get tenant() {
        if (!this._agent) {
            throw new Error('AgentIdentityApi: The agent must be set to perform tenant specific actions.');
        }
        return this._agent.agentDid.uri;
    }
    create(_a) {
        return __awaiter(this, arguments, void 0, function* ({ metadata, didMethod = 'dht', didOptions, store }) {
            const bearerDid = yield this.agent.did.create({
                method: didMethod,
                options: didOptions,
                tenant: this.tenant,
                store,
            });
            // Create the BearerIdentity object.
            const identity = new BearerIdentity({
                did: bearerDid,
                metadata: Object.assign(Object.assign({}, metadata), { uri: bearerDid.uri, tenant: this.tenant })
            });
            // Persist the Identity to the store, by default, unless the `store` option is set to false.
            if (store !== null && store !== void 0 ? store : true) {
                yield this._store.set({
                    id: identity.did.uri,
                    data: identity.metadata,
                    agent: this.agent,
                    tenant: identity.metadata.tenant,
                    preventDuplicates: false,
                    useCache: true
                });
            }
            return identity;
        });
    }
    export(_a) {
        return __awaiter(this, arguments, void 0, function* ({ didUri }) {
            const bearerIdentity = yield this.get({ didUri });
            if (!bearerIdentity) {
                throw new Error(`AgentIdentityApi: Failed to export due to Identity not found: ${didUri}`);
            }
            // If the Identity was found, return the Identity in a portable format, and if supported by the
            // Agent's key manager, the private key material.
            const portableIdentity = yield bearerIdentity.export();
            return portableIdentity;
        });
    }
    get(_a) {
        return __awaiter(this, arguments, void 0, function* ({ didUri }) {
            const storedIdentity = yield this._store.get({ id: didUri, agent: this.agent, useCache: true });
            // If the Identity is not found in the store, return undefined.
            if (!storedIdentity) {
                return undefined;
            }
            // Retrieve the DID from the Agent's DID store using the tenant value from the stored
            // Identity's metadata.
            const storedDid = yield this.agent.did.get({ didUri, tenant: storedIdentity.tenant });
            // If the Identity is present but the DID is not found, throw an error.
            if (!storedDid) {
                throw new Error(`AgentIdentityApi: Identity is present in the store but DID is missing: ${didUri}`);
            }
            // Create the BearerIdentity object.
            const identity = new BearerIdentity({ did: storedDid, metadata: storedIdentity });
            return identity;
        });
    }
    import(_a) {
        return __awaiter(this, arguments, void 0, function* ({ portableIdentity }) {
            // set the tenant of the portable identity to the agent's tenant
            portableIdentity.metadata.tenant = this.tenant;
            // Import the PortableDid to the Agent's DID store.
            const storedDid = yield this.agent.did.import({
                portableDid: portableIdentity.portableDid,
                tenant: portableIdentity.metadata.tenant
            });
            // Verify the DID is present in the Agent's DID store.
            if (!storedDid) {
                throw new Error(`AgentIdentityApi: Failed to import Identity: ${portableIdentity.metadata.uri}`);
            }
            // Create the BearerIdentity object.
            const identity = new BearerIdentity({ did: storedDid, metadata: portableIdentity.metadata });
            // Store the Identity metadata in the Agent's Identity store.
            yield this._store.set({
                id: identity.did.uri,
                data: identity.metadata,
                agent: this.agent,
                tenant: identity.metadata.tenant,
                preventDuplicates: true,
                useCache: true
            });
            return identity;
        });
    }
    list() {
        return __awaiter(this, arguments, void 0, function* ({ tenant } = {}) {
            // Retrieve the list of Identities from the Agent's Identity store.
            const storedIdentities = yield this._store.list({ agent: this.agent, tenant });
            const identities = yield Promise.all(storedIdentities.map(metadata => this.get({ didUri: metadata.uri })));
            return identities.filter(identity => typeof identity !== 'undefined');
        });
    }
    delete(_a) {
        return __awaiter(this, arguments, void 0, function* ({ didUri }) {
            const storedIdentity = yield this._store.get({ id: didUri, agent: this.agent, useCache: true });
            if (!storedIdentity) {
                throw new Error(`AgentIdentityApi: Failed to purge due to Identity not found: ${didUri}`);
            }
            // Delete the Identity from the Agent's Identity store.
            yield this._store.delete({ id: didUri, agent: this.agent });
        });
    }
    /**
     * Returns the DWN endpoints for the given DID.
     *
     * @param didUri - The DID URI to get the DWN endpoints for.
     * @returns An array of DWN endpoints.
     * @throws An error if the DID is not found, or no DWN service exists.
     */
    getDwnEndpoints({ didUri }) {
        return getDwnServiceEndpointUrls(didUri, this.agent.did);
    }
    /**
     * Sets the DWN endpoints for the given DID.
     *
     * @param didUri - The DID URI to set the DWN endpoints for.
     * @param endpoints - The array of DWN endpoints to set.
     * @throws An error if the DID is not found, or if an update cannot be performed.
     */
    setDwnEndpoints(_a) {
        return __awaiter(this, arguments, void 0, function* ({ didUri, endpoints }) {
            var _b;
            const bearerDid = yield this.agent.did.get({ didUri });
            if (!bearerDid) {
                throw new Error(`AgentIdentityApi: Failed to set DWN endpoints due to DID not found: ${didUri}`);
            }
            const portableDid = yield bearerDid.export();
            const dwnService = (_b = portableDid.document.service) === null || _b === void 0 ? void 0 : _b.find(service => service.id.endsWith('dwn'));
            if (dwnService) {
                // Update the existing DWN Service with the provided endpoints
                dwnService.serviceEndpoint = endpoints;
            }
            else {
                // create a DWN Service to add to the DID document
                const newDwnService = {
                    id: 'dwn',
                    type: 'DecentralizedWebNode',
                    serviceEndpoint: endpoints,
                };
                // if no other services exist, create a new array with the DWN service
                if (!portableDid.document.service) {
                    portableDid.document.service = [newDwnService];
                }
                else {
                    // otherwise, push the new DWN service to the existing services
                    portableDid.document.service.push(newDwnService);
                }
            }
            yield this.agent.did.update({ portableDid, tenant: this.agent.agentDid.uri });
        });
    }
    /**
     * Updates the Identity's metadata name field.
     *
     * @param didUri - The DID URI of the Identity to update.
     * @param name - The new name to set for the Identity.
     *
     * @throws An error if the Identity is not found, name is not provided, or no changes are detected.
     */
    setMetadataName(_a) {
        return __awaiter(this, arguments, void 0, function* ({ didUri, name }) {
            if (!name) {
                throw new Error('AgentIdentityApi: Failed to set metadata name due to missing name value.');
            }
            const identity = yield this.get({ didUri });
            if (!identity) {
                throw new Error(`AgentIdentityApi: Failed to set metadata name due to Identity not found: ${didUri}`);
            }
            if (identity.metadata.name === name) {
                throw new Error('AgentIdentityApi: No changes detected.');
            }
            // Update the name in the Identity's metadata and store it
            yield this._store.set({
                id: identity.did.uri,
                data: Object.assign(Object.assign({}, identity.metadata), { name }),
                agent: this.agent,
                tenant: identity.metadata.tenant,
                updateExisting: true,
                useCache: true
            });
        });
    }
    /**
     * Returns the connected Identity, if one is available.
     *
     * Accepts optional `connectedDid` parameter to filter the a specific connected identity,
     * if none is provided the first connected identity is returned.
     */
    connectedIdentity() {
        return __awaiter(this, arguments, void 0, function* ({ connectedDid } = {}) {
            const identities = yield this.list();
            if (identities.length < 1) {
                return undefined;
            }
            // If a specific connected DID is provided, return the first identity that matches it.
            // Otherwise, return the first connected identity.
            return connectedDid ?
                identities.find(identity => identity.metadata.connectedDid === connectedDid) :
                identities.find(identity => identity.metadata.connectedDid !== undefined);
        });
    }
}
//# sourceMappingURL=identity-api.js.map