"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SharedLinkService = void 0;
const common_1 = require("@nestjs/common");
const asset_ids_response_dto_1 = require("../dtos/asset-ids.response.dto");
const shared_link_dto_1 = require("../dtos/shared-link.dto");
const enum_1 = require("../enum");
const base_service_1 = require("./base.service");
const misc_1 = require("../utils/misc");
let SharedLinkService = class SharedLinkService extends base_service_1.BaseService {
    async getAll(auth, { id, albumId }) {
        return this.sharedLinkRepository
            .getAll({ userId: auth.user.id, id, albumId })
            .then((links) => links.map((link) => (0, shared_link_dto_1.mapSharedLink)(link, { stripAssetMetadata: false })));
    }
    async login(auth, dto) {
        if (!auth.sharedLink) {
            throw new common_1.ForbiddenException();
        }
        const sharedLink = await this.findOrFail(auth.user.id, auth.sharedLink.id);
        const { id, password } = sharedLink;
        if (!password) {
            throw new common_1.BadRequestException('Shared link is not password protected');
        }
        if (password !== dto.password) {
            throw new common_1.UnauthorizedException('Invalid password');
        }
        return {
            sharedLink: (0, shared_link_dto_1.mapSharedLink)(sharedLink, { stripAssetMetadata: !sharedLink.showExif }),
            token: this.asToken({ id, password }),
        };
    }
    async getMine(auth, authTokens) {
        if (!auth.sharedLink) {
            throw new common_1.ForbiddenException();
        }
        const sharedLink = await this.findOrFail(auth.user.id, auth.sharedLink.id);
        const { id, password } = sharedLink;
        if (password && !authTokens.includes(this.asToken({ id, password }))) {
            throw new common_1.UnauthorizedException('Password required');
        }
        return (0, shared_link_dto_1.mapSharedLink)(sharedLink, { stripAssetMetadata: !sharedLink.showExif });
    }
    async get(auth, id) {
        const sharedLink = await this.findOrFail(auth.user.id, id);
        return (0, shared_link_dto_1.mapSharedLink)(sharedLink, { stripAssetMetadata: false });
    }
    async create(auth, dto) {
        switch (dto.type) {
            case enum_1.SharedLinkType.Album: {
                if (!dto.albumId) {
                    throw new common_1.BadRequestException('Invalid albumId');
                }
                await this.requireAccess({ auth, permission: enum_1.Permission.AlbumShare, ids: [dto.albumId] });
                break;
            }
            case enum_1.SharedLinkType.Individual: {
                if (!dto.assetIds || dto.assetIds.length === 0) {
                    throw new common_1.BadRequestException('Invalid assetIds');
                }
                await this.requireAccess({ auth, permission: enum_1.Permission.AssetShare, ids: dto.assetIds });
                break;
            }
        }
        try {
            const sharedLink = await this.sharedLinkRepository.create({
                key: this.cryptoRepository.randomBytes(50),
                userId: auth.user.id,
                type: dto.type,
                albumId: dto.albumId || null,
                assetIds: dto.assetIds,
                description: dto.description || null,
                password: dto.password,
                expiresAt: dto.expiresAt || null,
                allowUpload: dto.allowUpload ?? true,
                allowDownload: dto.showMetadata === false ? false : (dto.allowDownload ?? true),
                showExif: dto.showMetadata ?? true,
                slug: dto.slug || null,
            });
            return (0, shared_link_dto_1.mapSharedLink)(sharedLink, { stripAssetMetadata: false });
        }
        catch (error) {
            this.handleError(error);
        }
    }
    handleError(error) {
        if (error.constraint_name === 'shared_link_slug_uq') {
            this.logger.debug('Shared link with this slug already exists');
            throw new common_1.BadRequestException('Failed to save shared link');
        }
        throw error;
    }
    async update(auth, id, dto) {
        await this.findOrFail(auth.user.id, id);
        try {
            const sharedLink = await this.sharedLinkRepository.update({
                id,
                userId: auth.user.id,
                description: dto.description,
                password: dto.password,
                expiresAt: dto.expiresAt,
                allowUpload: dto.allowUpload,
                allowDownload: dto.allowDownload,
                showExif: dto.showMetadata,
                slug: dto.slug || null,
            });
            return (0, shared_link_dto_1.mapSharedLink)(sharedLink, { stripAssetMetadata: false });
        }
        catch (error) {
            this.handleError(error);
        }
    }
    async remove(auth, id) {
        const sharedLink = await this.findOrFail(auth.user.id, id);
        await this.sharedLinkRepository.remove(sharedLink.id);
    }
    async findOrFail(userId, id) {
        const sharedLink = await this.sharedLinkRepository.get(userId, id);
        if (!sharedLink) {
            throw new common_1.BadRequestException('Shared link not found');
        }
        return sharedLink;
    }
    async addAssets(auth, id, dto) {
        const sharedLink = await this.findOrFail(auth.user.id, id);
        if (sharedLink.type !== enum_1.SharedLinkType.Individual) {
            throw new common_1.BadRequestException('Invalid shared link type');
        }
        const existingAssetIds = new Set(sharedLink.assets.map((asset) => asset.id));
        const notPresentAssetIds = dto.assetIds.filter((assetId) => !existingAssetIds.has(assetId));
        const allowedAssetIds = await this.checkAccess({
            auth,
            permission: enum_1.Permission.AssetShare,
            ids: notPresentAssetIds,
        });
        const results = [];
        for (const assetId of dto.assetIds) {
            const hasAsset = existingAssetIds.has(assetId);
            if (hasAsset) {
                results.push({ assetId, success: false, error: asset_ids_response_dto_1.AssetIdErrorReason.DUPLICATE });
                continue;
            }
            const hasAccess = allowedAssetIds.has(assetId);
            if (!hasAccess) {
                results.push({ assetId, success: false, error: asset_ids_response_dto_1.AssetIdErrorReason.NO_PERMISSION });
                continue;
            }
            results.push({ assetId, success: true });
        }
        await this.sharedLinkRepository.update({
            ...sharedLink,
            assetIds: results.filter(({ success }) => success).map(({ assetId }) => assetId),
        });
        return results;
    }
    async removeAssets(auth, id, dto) {
        const sharedLink = await this.findOrFail(auth.user.id, id);
        if (sharedLink.type !== enum_1.SharedLinkType.Individual) {
            throw new common_1.BadRequestException('Invalid shared link type');
        }
        const removedAssetIds = await this.sharedLinkAssetRepository.remove(id, dto.assetIds);
        const results = [];
        for (const assetId of dto.assetIds) {
            const wasRemoved = removedAssetIds.find((id) => id === assetId);
            if (!wasRemoved) {
                results.push({ assetId, success: false, error: asset_ids_response_dto_1.AssetIdErrorReason.NOT_FOUND });
                continue;
            }
            results.push({ assetId, success: true });
            sharedLink.assets = sharedLink.assets.filter((asset) => asset.id !== assetId);
        }
        await this.sharedLinkRepository.update(sharedLink);
        return results;
    }
    async getMetadataTags(auth, defaultDomain) {
        if (!auth.sharedLink || auth.sharedLink.password) {
            return null;
        }
        const config = await this.getConfig({ withCache: true });
        const sharedLink = await this.findOrFail(auth.sharedLink.userId, auth.sharedLink.id);
        const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id;
        const assetCount = sharedLink.assets.length > 0 ? sharedLink.assets.length : sharedLink.album?.assets?.length || 0;
        const imagePath = assetId
            ? `/api/assets/${assetId}/thumbnail?key=${sharedLink.key.toString('base64url')}`
            : '/feature-panel.png';
        return {
            title: sharedLink.album ? sharedLink.album.albumName : 'Public Share',
            description: sharedLink.description || `${assetCount} shared photos & videos`,
            imageUrl: new URL(imagePath, (0, misc_1.getExternalDomain)(config.server, defaultDomain)).href,
        };
    }
    asToken(sharedLink) {
        return this.cryptoRepository.hashSha256(`${sharedLink.id}-${sharedLink.password}`).toString('base64');
    }
};
exports.SharedLinkService = SharedLinkService;
exports.SharedLinkService = SharedLinkService = __decorate([
    (0, common_1.Injectable)()
], SharedLinkService);
//# sourceMappingURL=shared-link.service.js.map