# Copyright (C) Innuos
# SPDX-License-Identifier: GPL-2.0

package Plugins::PluginManagerAPI::Controllers::Plugins;

use warnings;
use strict;

use Slim::Utils::Log;
use Slim::Utils::PluginManager;
use Slim::Utils::PluginDownloader;
use Slim::Utils::Misc;
use Slim::Utils::OSDetect;
use Slim::Plugin::Extensions::Plugin;
use Slim::Utils::Prefs;


use Data::Dumper;
use JSON::XS::VersionOneAndTwo;
use Scalar::Util qw(blessed);

use Plugins::PluginManagerAPI::Utilities;

my $extprefs = preferences('plugin.extensions');


my $log = logger('plugin.PluginManagerAPI');


sub getEnabledPlugins {
	my ($action, $input, $callback) = @_;
	main::DEBUGLOG && $log->is_debug && $log->debug('getEnabledPlugins++');

	my $enabledPlugins = [];

	if ( my @plugins = Slim::Utils::PluginManager->enabledPlugins() ) {

		main::DEBUGLOG && $log->is_debug && $log->debug("Installed Plugins : " . Dumper(@plugins));

		push @$enabledPlugins, @plugins;

	}

	my $json = Plugins::PluginManagerAPI::Utilities::encodeReturnJSON($enabledPlugins);

	$callback->(Plugins::PluginManagerAPI::Utilities::HTTP_OK, $json);

	main::DEBUGLOG && $log->is_debug && $log->debug('getEnabledPlugins--');
	return;
}


sub getAllPlugins {
	my ($action, $input, $callback) = @_;
	main::DEBUGLOG && $log->is_debug && $log->debug('getAllPlugins++');

	if ( my $allPlugins = Slim::Utils::PluginManager->allPlugins() ) {

		main::DEBUGLOG && $log->is_debug && $log->debug("All Plugins : " . Dumper($allPlugins));

		my $result = [];

		foreach my $val (values %$allPlugins) {
			push @$result, $val;
		}		

		my $json = Plugins::PluginManagerAPI::Utilities::encodeReturnJSON($result);
		$callback->(Plugins::PluginManagerAPI::Utilities::HTTP_OK, $json);

	} else {
		$log->warn('Failed to retrieve plugin list');
		Plugins::PluginManagerAPI::Utilities::callbackError( $callback, "Unable to retrieve plugin list", Plugins::PluginManagerAPI::Utilities::HTTP_INTERNAL_SERVER_ERROR);
	}

	main::DEBUGLOG && $log->is_debug && $log->debug('getAllPlugins--');
	return;
}


sub getPluginDetails {
	my ($action, $input, $callback) = @_;
	my $pluginid = Slim::Utils::Misc::unescape($action->{'pluginid'});
	main::DEBUGLOG && $log->is_debug && $log->debug('getPluginDetails++' . $pluginid);

	if ( Slim::Utils::PluginManager->isEnabled($pluginid) ) {

		my $pluginData = Slim::Utils::PluginManager->dataForPlugin($pluginid);
		main::DEBUGLOG && $log->is_debug && $log->debug("Plugin Data : " . Dumper($pluginData));

		my $json = Plugins::PluginManagerAPI::Utilities::encodeReturnJSON($pluginData);
		$callback->(Plugins::PluginManagerAPI::Utilities::HTTP_OK, $json);

	} else {

		$log->warn("Plugin $pluginid not enabled");
		Plugins::PluginManagerAPI::Utilities::callbackError( $callback, "Plugin '$pluginid' not enabled or not found", Plugins::PluginManagerAPI::Utilities::HTTP_NOT_FOUND);

	}

	main::DEBUGLOG && $log->is_debug && $log->debug('getPluginDetails--');
	return;
}


sub installPlugin {
	my ($action, $input, $callback) = @_;
	my $pluginid = Slim::Utils::Misc::unescape($action->{'pluginid'});
	main::DEBUGLOG && $log->is_debug && $log->debug('installPlugin++' . $pluginid);

	if (my $plugin = Plugins::PluginManagerAPI::Utilities::extractPluginNameFromID($pluginid)) {
		if ( Slim::Utils::PluginManager->isEnabled($pluginid) ) {

			$log->warn("Plugin $pluginid already installed");
			Plugins::PluginManagerAPI::Utilities::callbackError( $callback, "Plugin '$pluginid' is already installed", Plugins::PluginManagerAPI::Utilities::HTTP_BAD_REQUEST);

		} else {
			# Is it a standard plugin?
			if (Plugins::PluginManagerAPI::Utilities::isSlimPlugin($pluginid)) {
				
				main::DEBUGLOG && $log->is_debug && $log->debug("Plugin $plugin enabled");				
				Slim::Utils::PluginManager->enablePlugin($plugin);
				$callback->(Plugins::PluginManagerAPI::Utilities::HTTP_OK, Plugins::PluginManagerAPI::Utilities::encodeSuccessJSON('ok, plugin queued for install'));

			} else {

				my $pluginCollection =  $extprefs->get('plugin');
				
				if (!$pluginCollection->{$plugin}) {
					
					#set it as an active plugin
					$pluginCollection->{$plugin} = 1;
					$extprefs->set('plugin', $pluginCollection);

					_processDownloadInstall($plugin);
					main::DEBUGLOG && $log->is_debug && $log->debug("Plugin $plugin set for install");
					$callback->(Plugins::PluginManagerAPI::Utilities::HTTP_OK, Plugins::PluginManagerAPI::Utilities::encodeSuccessJSON('ok, plugin queued for install'));

				} else {
					
					$log->warn("Plugin $pluginid could not be queued for install");
					Plugins::PluginManagerAPI::Utilities::callbackError( $callback, "Plugin '$pluginid' could not be queued for install", Plugins::PluginManagerAPI::Utilities::HTTP_BAD_REQUEST);
				
				}
				
			}
		}
	} else {

		$log->warn("$pluginid invalid pluginid");
		Plugins::PluginManagerAPI::Utilities::callbackError( $callback, "'$pluginid' is an invalid plugin id", Plugins::PluginManagerAPI::Utilities::HTTP_BAD_REQUEST);
	}

	main::DEBUGLOG && $log->is_debug && $log->debug('installPlugin--');
	return;
}


sub uninstallPlugin {
	my ($action, $input, $callback) = @_;
	my $pluginid = Slim::Utils::Misc::unescape($action->{'pluginid'});
	main::DEBUGLOG && $log->is_debug && $log->debug('uninstallPlugin++' . $pluginid);

	if (my $plugin = Plugins::PluginManagerAPI::Utilities::extractPluginNameFromID($pluginid)) {
		if ( Slim::Utils::PluginManager->isEnabled($pluginid) ) {
			#Should this be disabled or uninistalled?
			if ( Slim::Utils::PluginManager->allPlugins->{$plugin}->{'basedir'} !~ /InstalledPlugins/ ) {
				main::DEBUGLOG && $log->is_debug && $log->debug("Plugin $plugin disabled");
				Slim::Utils::PluginManager->disablePlugin($plugin);
			} else {
				main::DEBUGLOG && $log->is_debug && $log->debug("Plugin $plugin uninistalled");
				Slim::Utils::PluginDownloader::uninstall(undef, $plugin);
			}

			$callback->(Plugins::PluginManagerAPI::Utilities::HTTP_OK, Plugins::PluginManagerAPI::Utilities::encodeSuccessJSON('ok, plugin queued for uninstall'));

		} else {

			$log->warn("Plugin $pluginid not available for uninstall");
			Plugins::PluginManagerAPI::Utilities::callbackError( $callback, "Plugin '$pluginid' is not available for uninistall", Plugins::PluginManagerAPI::Utilities::HTTP_BAD_REQUEST);
		}
	} else {

		$log->warn("$pluginid invalid pluginid");
		Plugins::PluginManagerAPI::Utilities::callbackError( $callback, "'$pluginid' is an invalid plugin id", Plugins::PluginManagerAPI::Utilities::HTTP_BAD_REQUEST);
	}

	main::DEBUGLOG && $log->is_debug && $log->debug('uninstallPlugin--');
	return;
}

sub _processDownloadInstall {	
	my $plugin = shift;	

	#Taken from slim::plugins:extensions	

	# get plugin info from defined repos
	my $repos = Slim::Plugin::Extensions::Plugin->repos;

	my $data = { remaining => scalar keys %$repos, results => {}, errors => {} };

	

	for my $repo (keys %$repos) {
		Slim::Plugin::Extensions::Plugin::getExtensions({
			'name'   => $repo,
			'type'   => 'plugin',
			'target' => Slim::Utils::OSDetect::OS(),
			'version'=> $::VERSION,
			'lang'   => $Slim::Utils::Strings::currentLang,
			'details'=> 1,
			'cb'     => \&_processDownload,
			'pt'     => [ $data ],
			'onError'=> sub { $data->{'errors'}->{ $_[0] } = $_[1] },
		});
	}
}

sub _processDownload {
	my ($data, $resin, $info) = @_;

	if (scalar @$resin) {

		$data->{'results'}->{ $info->{'name'} } = {
			'title'   => $info->{'title'},
			'entries' => $resin,
			'weight'  => 1,
		};
	}

	#Taken from slim::plugins:extensions	
	my ($current, $active, $inactive, $hide) = Slim::Plugin::Extensions::Plugin::getCurrentPlugins();

	my @results = sort { $a->{'weight'} !=  $b->{'weight'} ?
						 $a->{'weight'} <=> $b->{'weight'} :
						 $a->{'title'} cmp $b->{'title'} } values %{$data->{'results'}};

	my @res;

	for my $res (@results) {
		push @res, @{$res->{'entries'}};
	}

	my $actions = Slim::Plugin::Extensions::Plugin::findUpdates(\@res, $current, $extprefs->get('plugin'), 'info');

	for my $plugin (keys %$actions) {
		
		my $entry = $actions->{$plugin};

		if ($entry->{'action'} eq 'install' && $entry->{'url'} && $entry->{'sha'}) {

			main::INFOLOG && $log->info("installing $plugin from $entry->{url}");

			Slim::Utils::PluginDownloader->install({ name => $plugin, url => $entry->{'url'}, sha => lc($entry->{'sha'}) });

		}
	}
}
1;
