import { IpcClient as SearchIpcClient } from "@autoplex-api/search";
import { PlexMovie } from "@autoplex/database";
import { InternalService } from "@autoplex/microservice";
import { sleep } from "@autoplex/utils";
import PlexLibrary from "./PlexLibrary";

/**
 * Throttle requests when updating the movie database
 */
const SCAN_THROTTLE = 500;

/**
 * How frequently to rescan the library
 */
const SCAN_FREQUENCY = 1000*60*60*24; // Rescan once a day

/**
 * A service to maintain the internal representation of the Plex library
 */
export default class LibraryScan extends InternalService
{
	/**
	 * The service name
	 */
	public readonly NAME = "Library Scan";

	/**
	 * Indicate if the plex library is currently being updated
	 */
	protected isUpdating: boolean = false;

	/**
	 * The scan interval
	 */
	protected scanInterval: NodeJS.Timeout|null = null;

	/**
	 * The Plex library service
	 */
	protected plex!: PlexLibrary;

	/**
	 * The movie/tv show search service
	 */
	protected search!: SearchIpcClient;

	/**
	 * Start the service
	 */
	public start() {
		this.plex = this.app.service<PlexLibrary>("Plex Library");
		this.search = this.app.service<SearchIpcClient>("Search");
		this.scanInterval = setInterval(this.scanLibraries.bind(this), SCAN_FREQUENCY);
		this.scanLibraries();
	}

	/**
	 * Shutdown the service
	 */
	public async shutdown() {
		if (this.scanInterval !== null) {
			clearInterval(this.scanInterval);
		}
	}

	/**
	 * Update the Plex libraries
	 */
	protected async scanLibraries() {
		await this.updateMovies();
	}

	/**
	 * Update the movie catalog
	 */
	protected async updateMovies() {
		if (this.isUpdating) {
			return;
		}
		this.isUpdating = true;
		let start = Date.now();
		this.log("Updating movies...");

		// Fetch the current and new sets of IMDb IDs
		let [currentIds, newIds] = await Promise.all([
			PlexMovie.imdbSet(),
			this.plex.movieImdbIds(),
		]);

		// Calculate the updates
		let toInsert = new Set(Object.keys(newIds).filter(id => !currentIds.has(id)));
		let toRemove = new Set([...currentIds].filter(id => !(id in newIds)));

		// Remove ald Ids and insert new ones
		await PlexMovie.removeImdbSet(toRemove);
		await PlexMovie.insertMovies([...toInsert].map(imdbId => ({imdbId, plexKey: newIds[imdbId]})));

		// Update TMDb IDs
		for (let movie of await PlexMovie.find({ where: { tmdbId: null } })) {
			let result = await this.search.findMovieFromImdb(movie.imdbId);
			if (result !== null) {
				movie.tmdbId = result.tmdbId;
				await movie.save();
			}
			await sleep(SCAN_THROTTLE);
		}

		this.log("Movies updated. Took", (Date.now() - start)/1000, "seconds");
		this.isUpdating = false;
	}
}
