import type { BaseMovieProvider } from "./interfaces/movie";
import type { BasePersonProvider } from "./interfaces/person";
import type { BaseSeriesProvider } from "./interfaces/series";
import type { AnyProvider, ProviderType } from "./manifest";

/**
 * Registry for managing metadata providers from extensions
 * Provides centralized access to movie, series, and person providers
 */
export class MetadataProviderRegistry {
	private movieProviders = new Map<string, BaseMovieProvider>();
	private seriesProviders = new Map<string, BaseSeriesProvider>();
	private personProviders = new Map<string, BasePersonProvider>();

	/** Track which extensions provide which provider types */
	private extensionProviders = new Map<
		string,
		Map<ProviderType, AnyProvider>
	>();

	/**
	 * Register a movie metadata provider
	 * @param name - Unique identifier for this provider (typically extensionName.type)
	 * @param provider - Movie provider instance
	 */
	registerMovieProvider(name: string, provider: BaseMovieProvider): void {
		this.movieProviders.set(name, provider);
	}

	/**
	 * Register a TV series metadata provider
	 * @param name - Unique identifier for this provider (typically extensionName.type)
	 * @param provider - Series provider instance
	 */
	registerSeriesProvider(name: string, provider: BaseSeriesProvider): void {
		this.seriesProviders.set(name, provider);
	}

	/**
	 * Register a person metadata provider
	 * @param name - Unique identifier for this provider (typically extensionName.type)
	 * @param provider - Person provider instance
	 */
	registerPersonProvider(name: string, provider: BasePersonProvider): void {
		this.personProviders.set(name, provider);
	}

	/**
	 * Get all registered movie providers
	 * @returns Array of all movie provider instances
	 */
	getMovieProviders(): BaseMovieProvider[] {
		return Array.from(this.movieProviders.values());
	}

	/**
	 * Get all registered series providers
	 * @returns Array of all series provider instances
	 */
	getSeriesProviders(): BaseSeriesProvider[] {
		return Array.from(this.seriesProviders.values());
	}

	/**
	 * Get all registered person providers
	 * @returns Array of all person provider instances
	 */
	getPersonProviders(): BasePersonProvider[] {
		return Array.from(this.personProviders.values());
	}

	/**
	 * Get a specific movie provider by name
	 * @param name - Provider name (extensionName.type)
	 * @returns Movie provider instance or undefined
	 */
	getMovieProvider(name: string): BaseMovieProvider | undefined {
		return this.movieProviders.get(name);
	}

	/**
	 * Get a specific series provider by name
	 * @param name - Provider name (extensionName.type)
	 * @returns Series provider instance or undefined
	 */
	getSeriesProvider(name: string): BaseSeriesProvider | undefined {
		return this.seriesProviders.get(name);
	}

	/**
	 * Get a specific person provider by name
	 * @param name - Provider name (extensionName.type)
	 * @returns Person provider instance or undefined
	 */
	getPersonProvider(name: string): BasePersonProvider | undefined {
		return this.personProviders.get(name);
	}

	/**
	 * Check if an extension supports a specific provider type
	 * @param extensionName - Name of the extension
	 * @param type - Provider type to check
	 * @returns True if extension provides this type
	 */
	extensionSupports(extensionName: string, type: ProviderType): boolean {
		const providers = this.extensionProviders.get(extensionName);
		return providers?.has(type) ?? false;
	}

	/**
	 * Get all providers from a specific extension
	 * @param extensionName - Name of the extension
	 * @returns Map of provider types to provider instances, or undefined
	 */
	getProvidersByExtension(
		extensionName: string,
	): Map<ProviderType, AnyProvider> | undefined {
		return this.extensionProviders.get(extensionName);
	}

	/**
	 * Register all providers from an extension
	 * @param extensionName - Name of the extension
	 * @param providers - Map of provider types to instances
	 */
	registerExtension(
		extensionName: string,
		providers: Map<ProviderType, AnyProvider>,
	): void {
		this.extensionProviders.set(extensionName, providers);
	}

	/**
	 * Unregister all providers from an extension
	 * Removes providers from type-specific maps and extension tracking
	 * @param extensionName - Name of the extension to unregister
	 */
	unregisterExtension(extensionName: string): void {
		const providers = this.extensionProviders.get(extensionName);
		if (providers) {
			// Remove from type-specific registries
			for (const [type] of providers) {
				const key = `${extensionName}.${type}`;
				this.movieProviders.delete(key);
				this.seriesProviders.delete(key);
				this.personProviders.delete(key);
			}
			this.extensionProviders.delete(extensionName);
		}
	}

	/**
	 * Get list of all registered extension names
	 * @returns Array of extension names
	 */
	getRegisteredExtensions(): string[] {
		return Array.from(this.extensionProviders.keys());
	}

	/**
	 * Get count of providers by type
	 * @returns Object with counts for each provider type
	 */
	getProviderCounts(): {
		movie: number;
		series: number;
		person: number;
		total: number;
	} {
		const movie = this.movieProviders.size;
		const series = this.seriesProviders.size;
		const person = this.personProviders.size;

		return {
			movie,
			series,
			person,
			total: movie + series + person,
		};
	}

	/**
	 * Check if registry has any providers
	 * @returns True if at least one provider is registered
	 */
	hasProviders(): boolean {
		return (
			this.movieProviders.size > 0 ||
			this.seriesProviders.size > 0 ||
			this.personProviders.size > 0
		);
	}

	/**
	 * Clear all registered providers
	 * Warning: This removes all providers from the registry
	 */
	clear(): void {
		this.movieProviders.clear();
		this.seriesProviders.clear();
		this.personProviders.clear();
		this.extensionProviders.clear();
	}
}
