#!/usr/bin/perl

# SAFARI Core
use v5.12;
use lib "/home/faitht/www/cgi-bin/safari/lib/";
use SAFARI::Core;
our $core = SAFARI::Core->new( 'safariPath' => '/home/faitht/www/cgi-bin/safari' );
use strict;
use utf8;
use URI::Encode qw(uri_encode);

# Needed modules
use lib "/home/faitht/www/cgi-bin/";
use lib "/home/faitht/www/cgi-bin/safari/lib/";
use lib "/home/faitht/www/beta/";
use SAFARI::JSON;
use SAFARI::Data::Article;
use FaithTree::Common;
use FaithTree::Backend;
use URI::Encode;


&main::libConfInitialize;
&main::libConfAuthorize;
our $user = &FaithTree::Common::getUid;

&loadPage;

# Functions

sub loadPage { 
	my $fuzzyPhrase = shift;
	my $jsonReload = ($main::core->{'form'}->{'format'} eq 'json') ? 1 : 0;
	$fuzzyPhrase //= $main::core->{'form'}->{'fuzzyPhrase'};
	my $reload = ($fuzzyPhrase) ? 1 : 0; #Note if we're in second iteration.	
	my ($verseReference, $alternates, $infoMessage, @multiples, $safeFuzzyPhrase);

	# Build View Object
	my $view = &FaithTree::Common::createSafariView({ 'title' => "FaithTree.com TriSearch", 'location' => 'triSearch', 'siteId' => 5, 'footerScripts' => 'trisearchBot.js' });

	# See if we are doing a fuzzy search
	if ($fuzzyPhrase) {
		# Are we only wanting a cached hit or is this a JSON reload?
		# Try to get the answer
		($verseReference, $alternates) = &getFuzzyAnswer({ 'fuzzyPhrase' => $fuzzyPhrase, 'loadOpenAI' => $jsonReload });

		# Let's deal with unsafe fuzzy phrase code.
		$safeFuzzyPhrase = ($fuzzyPhrase) ? $main::core->stripInjectionAttempts($fuzzyPhrase) : undef;		
	
		# If we asked for cached only and need to wait for a reload, output spinner.
		unless ($verseReference or ($main::core->{'form'}->{'format'} eq 'json')) {
			my $output = '<p class="text-center">' . &FaithTree::Common::loaderGraphic . '</p>';
			$output .= '<script>var reloadFuzzyPhrase = "' . $safeFuzzyPhrase . '";</script>';
			&FaithTree::Common::outputToSafari({ 'viewObject' => $view, 'output' => $output });
			return 0;
		}
		
		# Inform the user of what we've done.
		if ($verseReference) {
			$infoMessage = "FaithTree's Bible Bot believes you are looking for $verseReference.";

			if (${ $alternates }[0] ne '') {
				$infoMessage .= " You may also want to look at ";	
				$infoMessage .= join (', ', @{ $alternates }[0...$#{ $alternates }-1]);
				$infoMessage .= ' and ' . ${ $alternates }[$#{ $alternates }] if ($#{ $alternates } > 0);
				$infoMessage .= '.';
			}
		}
		else {
			# No go.
			$infoMessage = "FaithTree's Bible Bot wasn't able to figure out an appropriate passage to recommend based on what you've told it.";
			
		}
		
		@multiples[0] = $verseReference;
		
	}
	else {
		$verseReference = $main::core->{'form'}->{'verseReference'};
		$verseReference =~ s/\+/ /g;	
		$verseReference =~ s/\.(\d)/\. \1/g;
		
		# Detect multiple verse search
		state $multiVerseRegEx = qr/(?:(?:,|;)?\s*and\s*|,\s*|;\s*)/;
		if ($verseReference =~ /$multiVerseRegEx/) { 
			@multiples = split (/$multiVerseRegEx/, $verseReference);
		}
		else {
			push (@multiples, $verseReference);
		}
	}
	
	my $bodyOutput;

	# Parse the verse
	my $bibleModule = SAFARI::Plugins::Church::Data::Bible->new(
			'sqlPrefix' => $main::config{'prefix'}
		);	

	my $parsedVerse = $bibleModule->parseVerse({ 'verse' => $multiples[0] });

	# Is at least the first verse valid or do we need to try a fuzzy search?
	if ($parsedVerse->{'error'}) {
		# Try a diatheke search before resorting to Bible Bot -- might just be a plain search
		my $plainResult;
		unless ($main::core->{'form'}->{'skipPlainSearch'}) {
			$plainResult = &getDiathekeAnswer({ 'searchPhrase' => $fuzzyPhrase // $verseReference });
		}	
	
		# Reload as fuzzy search unless Bible Bot Search is disabled or we have a plain result.
		if ((! $plainResult) and (! $reload) and ($main::core->{'form'}->{'bibleBotSearch'})) {
			&loadPage($main::core->{'form'}->{'verseReference'});
			return 1;
		}

		# We got a full text result earlier.
		if ($plainResult) {
			# If there is only one result, simply show it.
			if ($plainResult->{'numberOfResults'} < 1) {
				$parsedVerse = $bibleModule->parseVerse({ 'verse' => $plainResult->{'firstResult'} });
				say STDERR "My first result: " . $plainResult->{'firstResult'};
				# If parsing works, set the first and only result.
				unless ($parsedVerse->{'error'}) {
					$multiples[0] = $plainResult->{'firstResult'};
				}
				
			}
			else {
				# Otherwise: Multiple results, so show list.
				my $url = 'https://grow.faithtree.com/bible/?fuzzyPhrase=' . uri_encode(( $fuzzyPhrase // $verseReference )) . '&skipPlainSearch=1&bibleBotSearch=1';
				$bodyOutput .= '<div class="row"><div class="col-sm-12 myPageCenterColumn">' . &FaithTree::Common::drawWidgetBox({ 'id' => 'bibleReading', 'title' => 'Search Results', 'contents' => '<div class="post"><p>The following verses have the term you searched for (<a href="' . $url . '">click here to do a Bible Bot AI search instead</a>): </p><ul>' . $plainResult->{'output'} . '</ul></div>'}) . '</div></div>';	
			}
		}
		else {
			# Report Error.
			my $url = $main::loader->generateURL({ 'patternRequested' => 'search', 'replacements' => { 'fullText' => uri_encode( $verseReference )  }, 'sid' => $main::loader->{'currentSid'} });
			$bodyOutput .= '<div class="row"><div class="col-sm-12 myPageCenterColumn">' . &FaithTree::Common::drawWidgetBox({ 'id' => 'bibleReading', 'title' => 'Search Error', 'contents' => '<div class="post"><p>No results found. Were you trying to do a <a href="' . $url . '">full text search</a> of the devotional archive instead?</p></div>'}) . '</div></div>';
		}
	}	
	
	# We test a second time, because this may have gotten fixed in last stage.
	unless ($parsedVerse->{'error'}) {
		# Get passages.
		for (my $i = 0; $i <= $#multiples; $i++) {
			last if ($i == 4); # Max of 4 searches at once.
			my $output = &loadBible({ 'verseReference' => $multiples[$i], 'bibleModule' => $bibleModule, 'view' => $view, 'number' => $i });
			$bodyOutput .= $output->{'bodyOutput'};
			$infoMessage .= $output->{'infoMessage'};
			#$categoryInfo = $output->{'categoryInfo'} if ($#multiples == 0);
		}
	}	
	
	# Create searchBox
	my $searchBoxValue = $safeFuzzyPhrase // $verseReference;
	my $searchBox = <<"		EOT";
		<div id="searchBox">
			<form action="https://grow.faithtree.com/bible/" method="get">
				<div class="row">
					<div class="col-xs-10">
						<input type="text" name="verseReference" value="$searchBoxValue" placeholder="Enter a Bible Reference" class="form-control">
					</div>
					<div class="col-xs-2">
						<button id="bibleBotSearchButton" type="button" class="btn btn-default btn-block" data-on-label="Bible Bot Search Enabled" data-off-label="Bible Bot Search Disabled">Loading...</button>
					</div>
				</div>
			</form>
		</div>
		EOT
		
	# Show info box if there is relevant info.
	$searchBox .= '<p>' . $infoMessage . '</p>' if $infoMessage;


	# Finalize output.
	#my $output = '<div class="row wideContent">' . $categoryInfo  . '</div>';
	my $output = '<div class="row"><div class="col-sm-12">' . &FaithTree::Common::drawWidgetBox({ 'id' => 'searchTop', 'title' => 'Search the Bible and Resources', 'contents' =>  $searchBox  }) . '</div></div>';
	$output .= $bodyOutput;
	$output .= '</div>';
	$output .= '</div>';

	if ($jsonReload and $fuzzyPhrase) {
		print &SAFARI::JSON::output({ output => { 'response' => $output } });	
	}
	else {
		&FaithTree::Common::outputToSafari({ 'viewObject' => $view, 'output' => $output });
	}
}

sub loadBible {
	my $params = shift;
	my $verseReference = $params->{'verseReference'};
	$verseReference =~ s/^\s+//g;
	my $bibleModule = $params->{'bibleModule'};
	my $view = $params->{'view'};	
	my $number = $params->{'number'};
	my $parsedVerse = $bibleModule->parseVerse({ 'verse' => $verseReference });

	# Fix for 2 Corinthians anomaly in NET.
	$parsedVerse->{'startVerse'}-- if ($parsedVerse->{'book'} == 47 and $parsedVerse->{'startChapter'} == 13 and $parsedVerse->{'startVerse'} >= 12);
	$parsedVerse->{'endVerse'}-- if ($parsedVerse->{'book'} == 47 and $parsedVerse->{'endChapter'} == 13 and $parsedVerse->{'endVerse'} >= 12);
	my $infoMessage = " <em>Note:</em> Your verse reference has been adjusted due to a difference in numbering in NET translation." if ($parsedVerse->{'book'} == 47 and (($parsedVerse->{'endChapter'} == 13 and $parsedVerse->{'endVerse'} >= 12) or ($parsedVerse->{'startChapter'} == 13 and $parsedVerse->{'startVerse'} >= 12)));

	# Do we want to show the full chapter?
	my $verseForSearch = $verseReference;
	if ($main::core->{'form'}->{'showChapter'}) {
		$verseForSearch = $parsedVerse->{'bookName'} . " " . $parsedVerse->{'startChapter'};
	}
	else {
		$verseForSearch = $bibleModule->generateVerseReference({ 'rawVerseReference' => $parsedVerse });		
	}

	# Get Bible text.
	my $results = &getBible({ 'verse' => $verseForSearch, 'version' => 'net', 'parsedVerse' => $parsedVerse });

	my $biblePassage;
	if (($results->{'error'}) or ($results->{'content'} =~ /^QUERY:/)) {
		$biblePassage = "<h2>Unable to Display the Passage</h2>";
		$biblePassage .= "<p>We are unable to display the requested passage.</p>";
	}
	else {
		$biblePassage = $results->{'content'};
		$biblePassage =~ s/<span class="verseNumber">([0-9]+)<\/span>/<a name="$1"><span class="verseNumber">$1<\/span><\/a>/sgi;
		$biblePassage .= '<p><small class="text-muted">The Scriptures quoted are from the <a href="http://netbible.com">NET Bible&reg;</a> copyright &copy; 1996, 2019, used with permission from Biblical Studies Press, L.L.C. All rights reserved.</small></p>'
	}
	
	# Get Related Articles
	my $siteId = "";
	my $startIndex = 0;
	my $range = 100;
	my $sort = 'gmt';
	my $relatedArticles = &loadArticles({ 'siteId' => $siteId, startIndex => $startIndex, range => $range, 'sort' => $sort, 'verseReference' => $verseForSearch });

	# Get final output
	my $pluginData = $main::loader->{'plugins'}->{'Church'}->main::front::categoryInformation;
	my $categoryInfo = $pluginData->[0]->{'params'}->{'categoryInfo'};
	$main::loader->{'plugins'}->{'Church'}->functionSet({ 'object' => $view , 'pluginData' => $pluginData });

	# Prep output.
	my $bodyOutput = '<div class="row"><div class="col-sm-8 myPageCenterColumn">';
	$bodyOutput .= &FaithTree::Common::drawWidgetBox({ 'id' => 'trisearchBot-' . $number, 'title' => 'Bible Bot for ' . $verseForSearch, 'contents' => '<div id="bibleBotOutput-' . $number . '" data-search-number="' . $number . '" data-verse-reference="' . $verseForSearch . '" class="col-xs-12"><center><div class="btn-group" role="group" id="bibleBotButtons-' . $number . '" data-search-number="' . $number . '" aria-label="..."><a  type="button" id="bot1-' . $number . '" class="btn botNumberButtons btn-default active" data-bot-number="1">Overview</a><a  type="button" id="bot2-' . $number . '" class="botNumberButtons btn btn-default" data-bot-number="2">X-Reference</a><a  type="button" id="bot3-' . $number . '" class="botNumberButtons btn btn-default" data-bot-number="3">Theology</a><a  type="button" id="bot4-' . $number . '" class="botNumberButtons btn btn-default" data-bot-number="4">Literature</a></div></center><div id="botContent-' . $number . '" class="botContent"><center>' . &FaithTree::Common::loaderGraphic() . '</center></div></div>'});
	$bodyOutput .= &FaithTree::Common::drawWidgetBox({ 'id' => 'bibleReading-' . $number, 'title' => $verseForSearch, 'contents' => '<div class="post">' . $biblePassage . '</div>'}) . '</div>';
	$bodyOutput .= '<div class="col-sm-4 myPageRightColumn"><div id="relatedArticles">' . &FaithTree::Common::drawWidgetBox({ 'id' => 'relatedDevotionals-' . $number, 'title' => 'Related Devotionals', 'contents' => '<div class="post">' . $relatedArticles . '</div>'}) . '</div></div></div>';
	$bodyOutput .= '<script>window.addEventListener(\'load\', function() { bibleBot("' . $number . '"); });</script>';
	
	return { 'bodyOutput' => $bodyOutput, 'categoryInfo' => $categoryInfo, 'infoMessage' =>  $infoMessage };		
}

sub getBible {
	my $params = shift;
	
	if ($params->{'version'} eq 'net') {
	
		# Setup to report to NET Bible
		my $uri = URI::Encode->new( { encode_reserved => 0 } );	
		my $encodedVerse = $uri->encode($params->{'verse'});
		unless ($params->{'parsedVerse'}->{'startChapter'}) {
			$encodedVerse .= '%201';
		}

		# Time to Fetch
		my $url = "http://labs.bible.org/api/?passage=" . $encodedVerse . "&formatting=full";

		# Fetch, check for errors and decode quote.
		return &FaithTree::Backend::fetchData({ 'url' => $url, 'disableFixEncoding' => 0 });
	
	}
	elsif ($params->{'version'} eq 'leb') {
		my $results;
		$results->{'content'} = `diatheke -b LEB -f text -k '$params->{'verse'}'`;	
		return $results;		
	}	
}

sub loadArticles {
	my $params = shift;
	
	my $article = SAFARI::Data::Article->new(
			'startIndex' => $params->{'startIndex'},
			'range' => $params->{'range'},
			'sortOrder' => $params->{'sort'},
			'sqlPrefix' => $main::config{'prefix'},
			'articleType' => $params->{'articleType'},
			'showDrafts' => 0, 		
			'siteId' => $params->{'siteId'},	
			'getReadCount' => 0		
		);  

	# Set stuff in accordance with plugins functions
	$params->{'pluginView'} = 'Church';
	$main::loader->{'plugins'}->{ $params->{'pluginView'} }->main::front::initializePluginView({ 'verseReference' => $params->{'verseReference'} });

	my $settings = $main::loader->{'plugins'}->{ $params->{'pluginView'} }->main::front::pluginView({ 'verseReference' => $params->{'verseReference'} });
	foreach my $setting (@{ $settings }) {
		my $function = $setting->{'function'};
		my $params = $setting->{'params'};
		$article->$function($params);
	}

	# Retrieve articles, then process HTML, Markdown, etc.
	my $output;
	$article->retrieve;
	my @articleData = $article->processArticleData;

	# Give the pluginView the opportunity to manipulate data.
	if ($params->{'pluginView'} and (exists $main::loader->{'plugins'}->{ $params->{'pluginView'} })) {
		@articleData = $main::loader->{'plugins'}->{ $params->{'pluginView'} }->main::front::processArticleData(@articleData);
	}


	for (my $i = 0; $i <= $#articleData; $i++) { 
		my $entry = $articleData[ $i ];
						
		#Get date data processed
		my ($postdate, $posttime);# = $view->getTimeDate($entry->{postTime});
	
		my $author;
	
		if ($entry->{'author'}->{'uid'}) {
			$author = $entry->{'author'};	
		}
		else {
			$author = $entry->{'poster'};
		}
	
		#output
		$output .= "<div class=\"post categoryEntry\"><h2><a href=\"/safari/article/" . $entry->{'aid'}  . ".html\">" . $entry->{'title'} . "</a></h2>";
		$output .= "<p>" . $entry->{'body'}->[0] . "</p></div>";
		
		# If there are more than 5... 
		if (($i == 4) and ($#articleData > 4)) {
			my $uri = URI::Encode->new( { encode_reserved => 0 } );	
			my $encodedVerse = $uri->encode($params->{'verseReference'});
			$output .= '<div class="post categoryEntry"><a href="https://faithtreecf.org/viewPlugin/Church/0.html?verseReference=' . $encodedVerse . '">More Results...</a></div>';
			last;
		}
	}	
	
	# Give error if needed.
	unless ($output) {
		$output .= '<div class="post categoryEntry"><h3>Search Tip</h3><p>No devotional materials were found related to the passage in your search. Searching a chapter (rather than just a verse) or a book (rather than just a chapter) may help turn up related devotionals for further study.</p></div>'
	}
	
	return $output;
	
}

sub getDiathekeAnswer {
	##Sample: Entries containing "testing"-- Exodus 17:7Deuteronomy 13:3 ; Ezekiel 21:13 ; Matthew 22:18 ; Mark 12:15 ; Luke 8:13 ; II Corinthians 8:8 ; Hebrews 3:8 ; James 1:3 ; James 1:12 ; Revelation of John 3:10 ;  -- 11 matches total (NETfree)

	my $params = shift;
	my $searchPhrase = substr($params->{'searchPhrase'},0,200);

	state $moduleStringShellQuote = require String::ShellQuote;
	my $escapedSearchPhrase = String::ShellQuote::shell_quote($searchPhrase);

	my $result = `diatheke -b NETfree -s phrase -f html -k '$escapedSearchPhrase'`;
	
	# Did we get anything?
	if ($result =~ /-- none/) {
		return;
	}
	
	# Clean up result.
	$result =~ s/([0-9])([A-Za-z])/\1 ; \2/;
	$result =~ s/III /3 /g;
	$result =~ s/II /2 /g;
	$result =~ s/I /1 /g;
	$result =~ s/Revelation of John/Revelation/g;	
	$result =~ s/^.*?--\s//;	
	$result =~ s/\s*?--.*?$//;
	my @results = split (/ ;\s*/, $result);	
	my $firstResult = $results[0];
	&makeListOfLinks(\@results);
	
	my $output = "\n";
	
	foreach (@results) {
		$output .= '<li>' . $_ . "</li>\n";
	}
	
	return { 'output' => $output, 'numberOfResults' => $#results, 'firstResult' => $firstResult };
}

sub getFuzzyAnswer {
	my $params = shift;
	my $fuzzySearchParam = substr($params->{'fuzzyPhrase'},0,200);
	my $sqlParam = $main::core->sqlSafe($fuzzySearchParam);
	
	my (@outputArray, $firstResult, $result);
	
	my $bibleBotFuzzySearchObject = $main::core->loadModule({ 'module' => 'FaithTree::Data::BibleBotFuzzySearch', 'createObject' => 1 });

	my $cachedResults = $bibleBotFuzzySearchObject->retrieve({ 'search' => $sqlParam, 'giveArray' => 1 });
	my ($result, @outputArray);

	# Get Bible module.
	my $bibleModule = SAFARI::Plugins::Church::Data::Bible->new(
			'sqlPrefix' => $main::config{'prefix'}
		);	
	
	# Have we cached?
	if (ref $cachedResults->[0] eq ref { }) {
		foreach (@{ $cachedResults }) {
			my $verseRef = $bibleModule->generateVerseReference({ 'rawVerseReference' => $_ });
			push (@outputArray, $verseRef) if $verseRef;
		}
	}
	elsif ($params->{'loadOpenAI'}) {
		# Get fresh result.
		my $prompts = [ 
				{ 'role' => "system", "content" => "For any prompt, you respond with a machine readable, comma seperated values formatted list of the verse reference(s) most likely relevant. If you do not know a relevant verse, you return a blank result." }, 
				{ 'role' => "user", "content" => "God loves the world." },
				{ 'role' => "assistant", "content" => "John 3:16, Phil. 2:4-12"},
				{ 'role' => "user", "content" => $fuzzySearchParam }
			];

		$result = &FaithTree::Common::queryOpenAI({ 'prompts' => $prompts });
		$result->{'content'} =~ s/\.$//;
		
		# Prepare for output.	
		my @bits = split(/,\s?/, $result->{'content'});
		
		# Save to cache if we have a result.
		if ($bits[0]) {
			my $i = 0;
			foreach (@bits) {
				my $parsedVerse = $bibleModule->parseVerse({ 'verse' => $_ });

				unless ($parsedVerse->{'error'}) {
					SAFARI::hashPush($parsedVerse, { 'search' => $sqlParam, 'version' => 1, 'entryNumber' => $i, 'type' => 'fuzzySearch', 'puid' => $user, 'unknown' => [ 'search', 'entryNumber', 'version', 'type' ] });
					$bibleBotFuzzySearchObject->save($parsedVerse);
					my $verseForSearch = $bibleModule->generateVerseReference({ 'rawVerseReference' => $parsedVerse });		
					push (@outputArray, $verseForSearch);
					$i++;
				}
			}
		}		
	}
	else {
		# We didn't have a cached result and we aren't OK'ed to get an external result.
		return 0;
	}

	# Main result.
	$firstResult = shift @outputArray;
	
	# Links to cross-reference.
	&makeListOfLinks(\@outputArray);
	
	return ($firstResult, \@outputArray);
}

sub makeListOfLinks {
	my $input = shift;

	# Links to cross-reference.
	foreach (@{ $input }) {
		next unless $_;
		$_ = '<a href="https://grow.faithtree.com/bible/' . uri_encode($_) . '">' . $_ . '</a>';
	}	
	
	return @_;
}