#!/usr/bin/env perl

use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin";
use SB::Defaults qw($SB_CONFIG $SB_PREFIX);
use SB::Utils qw(parseIni writeIni checkParameters configHashDifference changeOwner);
use SB::Update qw(updateModules updateTemplates updateLocales updateSkins);
use Getopt::Long;
use SB::Logging qw($Logger INFO DEBUG FATAL ERROR WARNING);
use POSIX qw(strftime setlocale LC_ALL);
require SB::Migration;
require SB::OS;
require SB::CPServer;

POSIX::setlocale(LC_ALL, "C");

sub printHelpItem {
	my ($option, $comment, $value, $boolean) = @_;
	if ($boolean) {
		if ($value) { $value = "yes"; }
		else { $value = "no"; }
	}
	format  =
@<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
" --$option",			 $comment . ($value ? " [$value]" : "")
.
	write;
}

sub printHelp($) {
	my $config = shift;
	print "Usage: \n";
	printHelpItem("phpcli_path", "PHP CLI executable", $config->{'internal'}->{'phpcli_path'});
	printHelpItem("phpcgi_path", "PHP CGI executable", $config->{'internal'}->{'phpcgi_path'});

	printHelpItem("update_locales", "Update locales", "no");
	printHelpItem("update_templates", "Update templates", "no");
	printHelpItem("update_modules", "Update modules", "no");
	printHelpItem("update_skins", "Update skins", "no");

	printHelpItem("migrate_all", "Migrate all sites", "no");
	printHelpItem("help", "Show this help", "no");
	printHelpItem("verbose", "Be verbose", "no");
	printHelpItem("[no]use_rewrite", "Enable/disable mod_rewrite usage", $config->{'general'}->{'use_rewrite'}, 1);

	printHelpItem("sb_admin_passwd", "Password for admin user", "not changed by default");
	
	printHelpItem("db_host", "MySQL database remote host", $config->{'database'}->{'host'});
	printHelpItem("db_port", "MySQL database remote port", $config->{'database'}->{'port'});
	printHelpItem("db_admin_name", "MySQL administrator username", $config->{'internal'}->{'db_admin_name'});
	printHelpItem("db_admin_passwd", "MySQL administrator password", $config->{'internal'}->{'db_admin_passwd'});

	printHelpItem("sb_db_user_name", "Sitebuilder MySQL username", $config->{'database'}->{'username'});
	printHelpItem("sb_db_user_passwd", "Sitebuilder MySQL password");
	printHelpItem("sb_db_name", "Sitebuilder database name", $config->{'database'}->{'dbname'});

#	printHelpItem("with_cp_server", "Use SW-CP-Server");
	printHelpItem("httpd_port", "Webserver port", $config->{'internal'}->{'httpd_port'});
	printHelpItem("sb_host_ip", "Create virtual host on the specified IP", $config->{'internal'}->{'sb_host_ip'});	
	printHelpItem("sb_hostname", "Sitebuilder virtual host name", $config->{'internal'}->{'sb_hostname'});
	printHelpItem("httpd_owner", "Webserver user and group", $config->{'internal'}->{'httpd_owner'});
	printHelpItem("[no]enable_ssl", "Enable/disable SSL", $config->{'internal'}->{'enable_ssl'}, 1);

	return;
}

sub updateWebServerConfig {
	my $status;
	my $with_cp_server = shift;
	$Logger->Log(INFO, "Creating webserver config");
	if ($with_cp_server) {
		$Logger->Log(INFO, "Using sw-cp-server configuration");
		SB::CPServer::makeLighttpdConfig();
	}
	else {
		$status = SB::Update::makeVHost();
		if ($status) {
			$Logger->Log(ERROR, "Webserver configuration failed");
		}
	}
}

sub updateDBUser {
	if (SB::Update::createSBDatabaseUser()) {
		$Logger->Log(FATAL, "Cannot update Sitebuilder database user");
	}
}

sub updateDBStructure {
	$Logger->Log(INFO, "Creating Sitebuilder database");
	my $ret = SB::Update::createSBDatabase();
	return unless $ret;
	if ($ret == 2) {
		$Logger->Log(INFO, "Database already exists");
	}
}

sub guessApplicationUrl($) {
	my $config = shift;
	my $hostname = $config->{'internal'}->{'sb_hostname'};
	my $port = $config->{'internal'}->{'httpd_port'};
	my $proto = 'http' . ($config->{'internal'}->{'enable_ssl'} ? 's' : '') . '://';
	return $proto . $hostname . ':' . $port;
}

sub execPhp($$) {
	my $config = shift;
	my $exec_php = shift;

	my $phpcli_path = $config->{"internal"}->{"phpcli_path"};
	$exec_php = $SB::Defaults::SB_PREFIX . "/" . $exec_php if (substr($exec_php, 0, 1) ne '/');
	exec ($phpcli_path, ($exec_php, @ARGV)) || die ("Cannot execute $exec_php: $!");
}

sub setHttpdPort($) {
	my $config = shift;

	my $use_ssl = $config->{'internal'}->{'enable_ssl'};
	my $port = \$config->{'internal'}->{'httpd_port'};

	if ($$port == 80 && $use_ssl) {
		$$port = 443;
	}

	if ($$port == 443 && !$use_ssl) {
		$$port = 80;
	}
}

sub main() {
	# Effective config
	my $config;

	# Command line config
	my $cmdLine = {};

	# Any config specified by --merge_config option
	my $oldConfig = {};

	# Load config
	my $currentConfig = parseIni($SB_CONFIG, {});

	my $help = 0;
	my $verbose = 0;
	my $update_templates = 0;
	my $update_modules = undef;
	my $update_skins = 0;
	my $update_locales = 0;
	my $without_restart = 0;
	my $force = 0;
	my $migrate_all = 0;
	my $force_write_apache = 0;
	my $force_create_db = 0;
	my $log_file;
	my $merge_config;
	my $skip_log_name = 0;
	my $exec_php;
	my $version = 0;
	my $with_cp_server = 0;

	$help = 1 unless @ARGV;

	Getopt::Long::Configure(qw(gnu_compat no_auto_abbrev));
	my $result = GetOptions(
		#php options
		"phpcli_path=s"		=> \$cmdLine->{'internal'}->{'phpcli_path'},
		"phpcgi_path=s"		=> \$cmdLine->{'internal'}->{'phpcgi_path'},

		#flags
		"without_restart"	=> \$without_restart, 	#obsolete
		"update_locales"	=> \$update_locales,
		"update_modules:s"	=> \$update_modules,
		"update_templates"	=> \$update_templates,
		"update_skins"		=> \$update_skins,
		"force"				=> \$force,				#deprecated
		"migrate_all"		=> \$migrate_all,
		"help"				=> \$help,
		"verbose"			=> \$verbose,

		"force_update_wscfg"=> \$force_write_apache, #internal
		"force_update_db"	=> \$force_create_db,    #internal
		"merge_config:s"	=> \$merge_config,  	 #internal
		"skiplogname"		=> \$skip_log_name, 	 #internal
		"logfile=s"			=> \$log_file,			 #internal

		#db options
		"db_host=s"			=> \$cmdLine->{'database'}->{'host'},
		"sb_db_user_name=s" => \$cmdLine->{'database'}->{'username'},
		"sb_db_user_passwd:s" => \$cmdLine->{'database'}->{'password'},
		"sb_db_name=s"		=> \$cmdLine->{'database'}->{'dbname'},
		"db_type=s"			=> \$cmdLine->{'database'}->{'adapter'}, # obsolete

		"db_port=s"			=> \$cmdLine->{'database'}->{'port'},
		"db_admin_name=s"	=> \$cmdLine->{'internal'}->{'db_admin_name'},
		"db_admin_passwd:s" => \$cmdLine->{'internal'}->{'db_admin_passwd'},
		"ext_ip=s"			=> \$cmdLine->{'internal'}->{'ext_ip'}, #obsolete?

		#admin password
		"sb_admin_passwd=s" => \$cmdLine->{'internal'}->{'sb_admin_passwd'},

		#httpd options
		"httpd_port=i"		=> \$cmdLine->{'internal'}->{'httpd_port'},
		"sb_hostname=s"		=> \$cmdLine->{'internal'}->{'sb_hostname'},
		"httpd_owner=s"		=> \$cmdLine->{'internal'}->{'httpd_owner'},
		"sb_host_ip=s"		=> \$cmdLine->{'internal'}->{'sb_host_ip'},
		"enable_ssl!"		=> \$cmdLine->{'internal'}->{'enable_ssl'},
		"use_rewrite!"		=> \$cmdLine->{'general'}->{'use_rewrite'},
		"with-cp-server|with_cp_server" => \$with_cp_server,

		"exec_php=s"		=> \$exec_php,
		"version"			=> \$version,
	);

	if (defined($merge_config)) {
		$merge_config = $SB_PREFIX . "/config.bkp" unless ($merge_config);

		if (-f $merge_config) {
			$oldConfig = parseIni($merge_config, {});
		}
	}

	# Make config copy to find out what changed
	$config = SB::Defaults::makeConfigCopy($SB::Defaults::CONFIG);
	SB::Utils::mergeConfig($config, $currentConfig);
	SB::Utils::mergeConfig($config, $oldConfig);
	SB::Utils::mergeConfig($config, $cmdLine);

	setHttpdPort($config);

	if ($help || !$result) {
		printHelp($config);
		return;
	}

	my $phpinidir = SB::OS::getInstallPrefix() . "/etc";
	$ENV{'PHPRC'} = $phpinidir;

	if ($exec_php) {
		execPhp($config, $exec_php);
		return;
	}

	unless ($log_file) {
		my $time = strftime "%y%m%d%H%M", localtime;
		$log_file = $SB::Defaults::SB_PREFIX . "/tmp/sb_config-$time.log";
	}

	$Logger->init($log_file);
	if ($verbose) {
		$Logger->setLevel(DEBUG);
	}

	if ($skip_log_name) {
		$Logger->setShowLogName(0);
	}

	unless ( -f $phpinidir . "/php.ini" ) {
		$Logger->Log(ERROR, "php.ini not found in $phpinidir, Sitebuilder may malfunction");
	}

	$Logger->Log(WARNING, "'--without_restart' option is obsolete") if $without_restart;
	$Logger->Log(WARNING, "'--force' option is deprecated") if $force;

	my $difference = configHashDifference($currentConfig, $config);
	my $dbUpdate = 0;
	my $apacheUpdate = 0;
	my $adminUpdate = 0;
	my $changeOwner = 0;

	$apacheUpdate ||= ($force_write_apache || $force || $with_cp_server);
	foreach my $key qw(internal:enable_ssl internal:httpd_port
						internal:sb_host_ip internal:phpcgi_path
						internal:sb_hostname) {
		last if ($apacheUpdate ||= exists($difference->{$key}));
	}
	
	$dbUpdate ||= ($force_create_db || $force);
	foreach my $key qw(database:host database:username database:dbname database:password) {
		last if ($dbUpdate ||= exists($difference->{$key}));
	}

	foreach my $key qw(internal:httpd_owner) {
		last if ($changeOwner ||= exists($difference->{$key})); 
	}

	foreach my $key qw(internal:sb_admin_passwd) {
		last if ($adminUpdate ||= exists($difference->{$key}));
	}

	if (exists($difference->{'internal:sb_hostname'})) {
		$config->{'general'}->{'application_url'} = guessApplicationUrl($config);
	}


	$SB::Defaults::CONFIG = $config;

#	while (my ($key, $value) = each(%$config)) {
#		tie %{$SB::Defaults::CONFIG->{$key}}, 'SB::ImmutableHash', $value;
#	}

	checkParameters();

	# Update database structure and users
	if ($dbUpdate) {
		updateDBUser();
		updateDBStructure();

		# After recreating database structure we need to upgrade product components as well
		$adminUpdate = 1;
		$update_skins = $update_locales = $update_templates = 1;
		unless (defined($update_modules)) {
			$update_modules = [ ];
		}
	}

	# Update SiteBuilder admin password
	if ($adminUpdate) {
		SB::Update::updateAdmin();
	}

	# Update webserver configuration file and reload the server
	if ($apacheUpdate) {
		updateWebServerConfig($with_cp_server);
	}

	$config->{'general'}->{'vz'} = (SB::Utils::detectVZ() ? 1 : 0);

	delete $config->{'internal'}->{'sb_admin_passwd'};
	delete $config->{'internal'}->{'db_admin_passwd'};
	delete $config->{'internal'}->{'db_admin_name'};
	delete $config->{'internal'}->{'force'};
	delete $config->{'internal'}->{'without_restart'};

	# Delete [sso] section to avoid confusion
	delete $config->{'sso'};

	# Create new SiteBuilder config
	writeIni($SB_CONFIG, $config);
	
	# Update config in /etc/swsoft/sitebuilder
	SB::Update::updateSitebuilderConf();

	if (defined($update_modules) && $update_modules eq '') {
		$update_modules = [ ];
	}
	
	if (defined($update_modules) && ref($update_modules) ne 'ARRAY') {
		my @update_modules = split(',', $update_modules);
		$update_modules = \@update_modules;
	}

	updateLocales() if $update_locales;

	if ( -e "$SB_PREFIX/migrate" ) {
		my $status = SB::Migration::performMigration();
		unlink("$SB_PREFIX/migrate") if $status == 0;
		$changeOwner = 1;
	}

	# update_modules MUST take place after migration
	updateModules($update_modules) if $update_modules;
	updateSkins() if $update_skins;
	updateTemplates() if $update_templates;

	if ($migrate_all) {
		$changeOwner = 1;
		SB::Migration::migrateAllSites();
	}

	if ($changeOwner) {
		SB::Utils::changeOwner($config->{'internal'}->{'httpd_owner'});
	}
}

main();
