"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const radarr_1 = __importDefault(require("../api/servarr/radarr"));
const sonarr_1 = __importDefault(require("../api/servarr/sonarr"));
const tautulli_1 = __importDefault(require("../api/tautulli"));
const themoviedb_1 = __importDefault(require("../api/themoviedb"));
const media_1 = require("../constants/media");
const datasource_1 = require("../datasource");
const Media_1 = __importDefault(require("../entity/Media"));
const Season_1 = __importDefault(require("../entity/Season"));
const User_1 = require("../entity/User");
const permissions_1 = require("../lib/permissions");
const settings_1 = require("../lib/settings");
const logger_1 = __importDefault(require("../logger"));
const auth_1 = require("../middleware/auth");
const express_1 = require("express");
const typeorm_1 = require("typeorm");
const mediaRoutes = (0, express_1.Router)();
mediaRoutes.get('/', async (req, res, next) => {
    const mediaRepository = (0, datasource_1.getRepository)(Media_1.default);
    const pageSize = req.query.take ? Number(req.query.take) : 20;
    const skip = req.query.skip ? Number(req.query.skip) : 0;
    let statusFilter = undefined;
    switch (req.query.filter) {
        case 'available':
            statusFilter = media_1.MediaStatus.AVAILABLE;
            break;
        case 'partial':
            statusFilter = media_1.MediaStatus.PARTIALLY_AVAILABLE;
            break;
        case 'allavailable':
            statusFilter = (0, typeorm_1.In)([
                media_1.MediaStatus.AVAILABLE,
                media_1.MediaStatus.PARTIALLY_AVAILABLE,
            ]);
            break;
        case 'processing':
            statusFilter = media_1.MediaStatus.PROCESSING;
            break;
        case 'pending':
            statusFilter = media_1.MediaStatus.PENDING;
            break;
    }
    let sortFilter = {
        id: 'DESC',
    };
    switch (req.query.sort) {
        case 'modified':
            sortFilter = {
                updatedAt: 'DESC',
            };
            break;
        case 'mediaAdded':
            sortFilter = {
                mediaAddedAt: 'DESC',
            };
    }
    let whereClause;
    if (statusFilter || req.query.sort === 'mediaAdded') {
        whereClause = {};
        if (statusFilter)
            whereClause.status = statusFilter;
        if (req.query.sort === 'mediaAdded')
            whereClause.mediaAddedAt = (0, typeorm_1.Not)((0, typeorm_1.IsNull)());
    }
    try {
        const [media, mediaCount] = await mediaRepository.findAndCount({
            order: sortFilter,
            where: whereClause,
            take: pageSize,
            skip,
        });
        return res.status(200).json({
            pageInfo: {
                pages: Math.ceil(mediaCount / pageSize),
                pageSize,
                results: mediaCount,
                page: Math.ceil(skip / pageSize) + 1,
            },
            results: media,
        });
    }
    catch (e) {
        next({ status: 500, message: e.message });
    }
});
mediaRoutes.post('/:id/:status', (0, auth_1.isAuthenticated)(permissions_1.Permission.MANAGE_REQUESTS), async (req, res, next) => {
    const mediaRepository = (0, datasource_1.getRepository)(Media_1.default);
    const seasonRepository = (0, datasource_1.getRepository)(Season_1.default);
    const media = await mediaRepository.findOne({
        where: { id: Number(req.params.id) },
    });
    if (!media) {
        return next({ status: 404, message: 'Media does not exist.' });
    }
    const is4k = String(req.body.is4k) === 'true';
    switch (req.params.status) {
        case 'available':
            media[is4k ? 'status4k' : 'status'] = media_1.MediaStatus.AVAILABLE;
            if (media.mediaType === media_1.MediaType.TV) {
                const expectedSeasons = req.body.seasons ?? [];
                for (const expectedSeason of expectedSeasons) {
                    let season = media.seasons.find((s) => s.seasonNumber === expectedSeason?.seasonNumber);
                    if (!season) {
                        // Create the season if it doesn't exist
                        season = seasonRepository.create({
                            seasonNumber: expectedSeason?.seasonNumber,
                        });
                        media.seasons.push(season);
                    }
                    season[is4k ? 'status4k' : 'status'] = media_1.MediaStatus.AVAILABLE;
                }
            }
            break;
        case 'partial':
            if (media.mediaType === media_1.MediaType.MOVIE) {
                return next({
                    status: 400,
                    message: 'Only series can be set to be partially available',
                });
            }
            media[is4k ? 'status4k' : 'status'] = media_1.MediaStatus.PARTIALLY_AVAILABLE;
            break;
        case 'processing':
            media[is4k ? 'status4k' : 'status'] = media_1.MediaStatus.PROCESSING;
            break;
        case 'pending':
            media[is4k ? 'status4k' : 'status'] = media_1.MediaStatus.PENDING;
            break;
        case 'unknown':
            media[is4k ? 'status4k' : 'status'] = media_1.MediaStatus.UNKNOWN;
    }
    await mediaRepository.save(media);
    return res.status(200).json(media);
});
mediaRoutes.delete('/:id', (0, auth_1.isAuthenticated)(permissions_1.Permission.MANAGE_REQUESTS), async (req, res, next) => {
    try {
        const mediaRepository = (0, datasource_1.getRepository)(Media_1.default);
        const media = await mediaRepository.findOneOrFail({
            where: { id: Number(req.params.id) },
        });
        if (media.status === media_1.MediaStatus.BLOCKLISTED) {
            media.resetServiceData();
            await mediaRepository.save(media);
        }
        else {
            await mediaRepository.remove(media);
        }
        return res.status(204).send();
    }
    catch (e) {
        if (e instanceof typeorm_1.EntityNotFoundError) {
            return res.status(204).send();
        }
        logger_1.default.error('Something went wrong deleting media', {
            label: 'Media',
            mediaId: req.params.id,
            message: e.message,
        });
        next({ status: 500, message: 'Failed to delete media' });
    }
});
mediaRoutes.delete('/:id/file', (0, auth_1.isAuthenticated)(permissions_1.Permission.MANAGE_REQUESTS), async (req, res, next) => {
    try {
        const settings = (0, settings_1.getSettings)();
        const mediaRepository = (0, datasource_1.getRepository)(Media_1.default);
        const media = await mediaRepository.findOneOrFail({
            where: { id: Number(req.params.id) },
        });
        const is4k = String(req.query.is4k) === 'true';
        const isMovie = media.mediaType === media_1.MediaType.MOVIE;
        let serviceSettings;
        if (isMovie) {
            serviceSettings = settings.radarr.find((radarr) => radarr.isDefault && radarr.is4k === is4k);
        }
        else {
            serviceSettings = settings.sonarr.find((sonarr) => sonarr.isDefault && sonarr.is4k === is4k);
        }
        const specificServiceId = is4k ? media.serviceId4k : media.serviceId;
        if (specificServiceId &&
            specificServiceId >= 0 &&
            serviceSettings?.id !== specificServiceId) {
            if (isMovie) {
                serviceSettings = settings.radarr.find((radarr) => radarr.id === specificServiceId);
            }
            else {
                serviceSettings = settings.sonarr.find((sonarr) => sonarr.id === specificServiceId);
            }
        }
        if (!serviceSettings) {
            const arrName = `${is4k ? '4K ' : ''}${isMovie ? 'Radarr' : 'Sonarr'}`;
            logger_1.default.info(`There is no default ${arrName} server configured. Did you set any of your ${arrName} servers as default?`, {
                label: 'Media Request',
                mediaId: media.id,
            });
            return next({
                status: 409,
                message: `No ${arrName} server configured to delete media files`,
            });
        }
        let service;
        if (isMovie) {
            service = new radarr_1.default({
                apiKey: serviceSettings?.apiKey,
                url: radarr_1.default.buildUrl(serviceSettings, '/api/v3'),
            });
        }
        else {
            service = new sonarr_1.default({
                apiKey: serviceSettings?.apiKey,
                url: sonarr_1.default.buildUrl(serviceSettings, '/api/v3'),
            });
        }
        if (isMovie) {
            await service.removeMovie(media.tmdbId);
        }
        else {
            const tmdb = new themoviedb_1.default();
            const series = await tmdb.getTvShow({ tvId: media.tmdbId });
            const tvdbId = series.external_ids.tvdb_id ?? media.tvdbId;
            if (!tvdbId) {
                throw new Error('TVDB ID not found');
            }
            await service.removeSeries(tvdbId);
            for (const season of media.seasons) {
                season[is4k ? 'status4k' : 'status'] = media_1.MediaStatus.DELETED;
            }
        }
        media[is4k ? 'status4k' : 'status'] = media_1.MediaStatus.DELETED;
        media.resetServiceData(is4k);
        await mediaRepository.save(media);
        return res.status(204).send();
    }
    catch (e) {
        if (e instanceof typeorm_1.EntityNotFoundError) {
            return next({ status: 404, message: 'Media not found' });
        }
        logger_1.default.error('Something went wrong deleting media file', {
            label: 'Media',
            mediaId: req.params.id,
            message: e.message,
        });
        next({ status: 500, message: 'Failed to delete media file' });
    }
});
mediaRoutes.get('/:id/watch_data', (0, auth_1.isAuthenticated)(permissions_1.Permission.ADMIN), async (req, res, next) => {
    const settings = (0, settings_1.getSettings)().tautulli;
    if (!settings.hostname || !settings.port || !settings.apiKey) {
        return next({
            status: 404,
            message: 'Tautulli API not configured.',
        });
    }
    const media = await (0, datasource_1.getRepository)(Media_1.default).findOne({
        where: { id: Number(req.params.id) },
    });
    if (!media) {
        return next({ status: 404, message: 'Media does not exist.' });
    }
    try {
        const tautulli = new tautulli_1.default(settings);
        const userRepository = (0, datasource_1.getRepository)(User_1.User);
        const response = {};
        if (media.ratingKey) {
            const watchStats = await tautulli.getMediaWatchStats(media.ratingKey);
            const watchUsers = await tautulli.getMediaWatchUsers(media.ratingKey);
            const plexIds = watchUsers.map((u) => u.user_id);
            if (!plexIds.length)
                plexIds.push(-1);
            const users = await userRepository
                .createQueryBuilder('user')
                .where('user.plexId IN (:...plexIds)', { plexIds })
                .getMany();
            const playCount = watchStats.find((i) => i.query_days == 0)?.total_plays ?? 0;
            const playCount7Days = watchStats.find((i) => i.query_days == 7)?.total_plays ?? 0;
            const playCount30Days = watchStats.find((i) => i.query_days == 30)?.total_plays ?? 0;
            response.data = {
                users: users,
                playCount,
                playCount7Days,
                playCount30Days,
            };
        }
        if (media.ratingKey4k) {
            const watchStats4k = await tautulli.getMediaWatchStats(media.ratingKey4k);
            const watchUsers4k = await tautulli.getMediaWatchUsers(media.ratingKey4k);
            const plexIds4k = watchUsers4k.map((u) => u.user_id);
            if (!plexIds4k.length)
                plexIds4k.push(-1);
            const users = await userRepository
                .createQueryBuilder('user')
                .where('user.plexId IN (:...plexIds)', { plexIds: plexIds4k })
                .getMany();
            const playCount = watchStats4k.find((i) => i.query_days == 0)?.total_plays ?? 0;
            const playCount7Days = watchStats4k.find((i) => i.query_days == 7)?.total_plays ?? 0;
            const playCount30Days = watchStats4k.find((i) => i.query_days == 30)?.total_plays ?? 0;
            response.data4k = {
                users,
                playCount,
                playCount7Days,
                playCount30Days,
            };
        }
        return res.status(200).json(response);
    }
    catch (e) {
        logger_1.default.error('Something went wrong fetching media watch data', {
            label: 'API',
            errorMessage: e.message,
            mediaId: req.params.id,
        });
        next({ status: 500, message: 'Failed to fetch watch data.' });
    }
});
exports.default = mediaRoutes;
