#import <CoreSpotlight/CSSearchableIndex.h>
#import <CoreSpotlight/CSSearchableItem.h>
#import <CoreSpotlight/CSSearchableItemAttributeSet.h>
#import <UIKit/UIKit.h>
#import <WebKit/WKDownload.h>
#import <WebKit/WKDownloadDelegate.h>
#import <WebKit/WKNavigationAction.h>
#import <WebKit/WKNavigationDelegate.h>
#import <WebKit/WKNavigationResponse.h>
#import <WebKit/WKUIDelegate.h>
#import <WebKit/WKWebView.h>
#import <WebKit/WKWebViewConfiguration.h>

#include "log.h"
#include "mem.h"
#include "util.js.h"

#include "uv.h"

#include <libgen.h>
#include <string.h>

static int s_port;

void tf_run_thread_start(const char* zip_path, const char* arguments);

@interface ViewController : UINavigationController <WKUIDelegate, WKNavigationDelegate, WKDownloadDelegate, UIDocumentPickerDelegate>
@property (strong, nonatomic) WKWebView* web_view;
@property bool initial_load_complete;
@property (retain) NSURL* download_url;
@end

static void _start_initial_load(WKWebView* web_view)
{
	[web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/login/auto", s_port]]]];
}

@implementation ViewController : UINavigationController
- (void)viewDidLoad
{
	[super viewDidLoad];

	WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
	self.web_view = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
	self.web_view.UIDelegate = self;
	self.web_view.navigationDelegate = self;
	self.web_view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
	self.web_view.translatesAutoresizingMaskIntoConstraints = false;
	self.web_view.allowsBackForwardNavigationGestures = true;
	[self.view addSubview:self.web_view];
	UIRefreshControl* refresh = [[UIRefreshControl alloc] init];
	[refresh addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
	self.web_view.scrollView.refreshControl = refresh;

	[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view
														  attribute:NSLayoutAttributeTop
														 multiplier:1.0
														   constant:0]];
	[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view
														  attribute:NSLayoutAttributeBottom
														 multiplier:1.0
														   constant:-75]];
	[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view
														  attribute:NSLayoutAttributeLeft
														 multiplier:1.0
														   constant:0]];
	[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view
														  attribute:NSLayoutAttributeRight
														 multiplier:1.0
														   constant:0]];

	_start_initial_load(self.web_view);
}

- (void)handleRefresh:(id)sender
{
	tf_printf("refresh\n");
	[self.web_view reload];
}

- (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation
{
	if (!self.initial_load_complete)
	{
		tf_printf("initial load complete\n");
		self.initial_load_complete = true;
	}
	self.navigationController.interactivePopGestureRecognizer.enabled = self.web_view.canGoBack;
	[self.web_view.scrollView.refreshControl endRefreshing];
}

- (void)webView:(WKWebView*)webView didFailProvisionalNavigation:(WKNavigation*)navigation withError:(NSError*)error
{
	if (!self.initial_load_complete)
	{
		_start_initial_load(self.web_view);
	}
}

- (void)webView:(WKWebView*)webView
	runJavaScriptConfirmPanelWithMessage:(NSString*)message
						initiatedByFrame:(WKFrameInfo*)frame
					   completionHandler:(void (^)(BOOL result))completionHandler
{
	UIAlertController* alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert];
	[alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) { completionHandler(true); }]];
	[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action) { completionHandler(false); }]];
	[self presentViewController:alertController animated:YES completion:^ {}];
}

- (void)webView:(WKWebView*)webView runJavaScriptAlertPanelWithMessage:(NSString*)message initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(void))completionHandler
{
	UIAlertController* alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert];
	[alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) { completionHandler(); }]];
	[self presentViewController:alertController animated:YES completion:^ {}];
}

- (void)webView:(WKWebView*)webView
	runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt
							  defaultText:(NSString*)defaultText
						 initiatedByFrame:(WKFrameInfo*)frame
						completionHandler:(void (^)(NSString*))completionHandler
{
	NSString* sender = [NSString stringWithFormat:@"%@", self.web_view.URL.host];

	UIAlertController* alertController = [UIAlertController alertControllerWithTitle:prompt message:sender preferredStyle:UIAlertControllerStyleAlert];
	[alertController addTextFieldWithConfigurationHandler:^(UITextField* textField) {
		textField.placeholder = defaultText;
		textField.text = defaultText;
	}];
	[alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) {
		NSString* input = ((UITextField*)alertController.textFields.firstObject).text;
		completionHandler(input);
	}]];
	[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action) { completionHandler(nil); }]];
	[self presentViewController:alertController animated:YES completion:^ {}];
}

- (void)webView:(WKWebView*)webView decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction decisionHandler:(void (^)(enum WKNavigationActionPolicy))decisionHandler
{
	decisionHandler(navigationAction.shouldPerformDownload ? WKNavigationActionPolicyDownload : WKNavigationActionPolicyAllow);
}

- (void)webView:(WKWebView*)webView
	decidePolicyForNavigationResponse:(WKNavigationResponse*)navigationResponse
					  decisionHandler:(void (^)(enum WKNavigationResponsePolicy))decisionHandler
{
	decisionHandler(navigationResponse.canShowMIMEType ? WKNavigationResponsePolicyAllow : WKNavigationResponsePolicyDownload);
}

- (void)webView:(WKWebView*)webView navigationAction:(WKNavigationAction*)navigationAction didBecomeDownload:(WKDownload*)download
{
	download.delegate = self;
}

- (void)webView:(WKWebView*)webView navigationResponse:(WKNavigationResponse*)navigationResponse didBecomeDownload:(WKDownload*)download
{
	download.delegate = self;
}

- (void)download:(WKDownload*)download
	decideDestinationUsingResponse:(NSURLResponse*)response
				 suggestedFilename:(NSString*)suggestedFilename
				 completionHandler:(void (^)(NSURL*))completionHandler
{
	self.download_url = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:suggestedFilename];
	completionHandler(self.download_url);
}

- (void)downloadDidFinish:(WKDownload*)download
{
	UIDocumentPickerViewController* picker = [[UIDocumentPickerViewController alloc] initForExportingURLs:@[ self.download_url ]];
	picker.delegate = self;
	[self presentViewController:picker animated:YES completion:nil];
}

- (void)download:(WKDownload*)download didFailWithError:(NSError*)error resumeData:(NSData*)resumeData
{
	tf_printf("download didFailWithError:%s\n", [error.localizedDescription UTF8String]);
}

- (void)documentPicker:(UIDocumentPickerViewController*)controller didPickDocumentAtURLs:(NSArray<NSURL*>*)urls
{
	[[NSFileManager defaultManager] removeItemAtURL:self.download_url error:nil];
}

- (void)documentPickerWasCancelled:(UIDocumentPickerViewController*)controller
{
	[[NSFileManager defaultManager] removeItemAtURL:self.download_url error:nil];
}
@end

@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@end

@implementation SceneDelegate
@synthesize window = _window;

static char* _reencode_for_url_component(const char* string)
{
	size_t length = strlen(string);
	char* copy = tf_malloc(length * 3 + 1);
	char* out = copy;
	for (const char* c = string; *c; c++)
	{
		if (*c == '-')
		{
			*out++ = '%';
			*out++ = '2';
			*out++ = 'b';
		}
		else if (*c == '_')
		{
			*out++ = '%';
			*out++ = '2';
			*out++ = 'f';
		}
		else
		{
			*out++ = *c;
		}
	}
	*out = '\0';
	return copy;
}

- (void)scene:(UIScene*)scene openURLContexts:(NSSet<UIOpenURLContext*>*)URLContexts
{
	UIOpenURLContext* context = [URLContexts anyObject];
	if (context)
	{
		NSCharacterSet* set = [NSCharacterSet characterSetWithCharactersInString:@":/"];
		NSArray* parts = [context.URL.resourceSpecifier componentsSeparatedByCharactersInSet:set];
		if (parts.count == 3)
		{
			if ([[parts objectAtIndex:0] isEqualToString:@"message"] && [[parts objectAtIndex:1] isEqualToString:@"classic"])
			{
				char* identifier = _reencode_for_url_component([[parts objectAtIndex:2] UTF8String]);
				char url[1024];
				snprintf(url, sizeof(url), "http://localhost:%d/~core/ssb/#%%%s.sha256", s_port, identifier);
				tf_free(identifier);
				tf_printf("Navigating to %s.\n", url);
				ViewController* view_controller = (ViewController*)self.window.rootViewController;
				[view_controller.web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithUTF8String:url]]]];
			}
			else if ([[parts objectAtIndex:0] isEqualToString:@"feed"] && [[parts objectAtIndex:1] isEqualToString:@"classic"])
			{
				char* identifier = _reencode_for_url_component([[parts objectAtIndex:2] UTF8String]);
				char url[1024];
				snprintf(url, sizeof(url), "http://localhost:%d/~core/ssb/#@%s.ed25519", s_port, identifier);
				tf_free(identifier);
				tf_printf("Navigating to %s.\n", url);
				ViewController* view_controller = (ViewController*)self.window.rootViewController;
				[view_controller.web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithUTF8String:url]]]];
			}
			else if ([[parts objectAtIndex:0] isEqualToString:@"blob"] && [[parts objectAtIndex:1] isEqualToString:@"classic"])
			{
				char* identifier = _reencode_for_url_component([[parts objectAtIndex:2] UTF8String]);
				char url[1024];
				snprintf(url, sizeof(url), "http://localhost:%d/~core/ssb/#&%s.sha256", s_port, identifier);
				tf_free(identifier);
				tf_printf("Navigating to %s.\n", url);
				ViewController* view_controller = (ViewController*)self.window.rootViewController;
				[view_controller.web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithUTF8String:url]]]];
			}
		}
	}
}

- (void)scene:(UIScene*)scene willConnectToSession:(UISceneSession*)session options:(UISceneConnectionOptions*)connectionOptions
{
	self.window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene*)scene];
	self.window.rootViewController = [[ViewController alloc] init];
	[self.window makeKeyAndVisible];
	[self scene:scene openURLContexts:connectionOptions.URLContexts];
}
@end

@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow* window;
@end

@implementation AppDelegate
- (UISceneConfiguration*)application:(UIApplication*)application
	configurationForConnectingSceneSession:(UISceneSession*)connectingSceneSession
								   options:(UISceneConnectionOptions*)optinos
{
	UISceneConfiguration* configuration = [[UISceneConfiguration alloc] initWithName:nil sessionRole:connectingSceneSession.role];
	configuration.delegateClass = SceneDelegate.class;
	return configuration;
}

- (BOOL)application:(UIApplication*)application
	continueUserActivity:(NSUserActivity*)activity
	  restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>>*))restorationHandler
{
	if ([activity.activityType isEqual:CSSearchableItemActionType])
	{
		const char* identifier = [[activity.userInfo valueForKey:CSSearchableItemActivityIdentifier] UTF8String];
		tf_printf("Jumping to search result: %s.\n", identifier);

		char url[1024];
		snprintf(url, sizeof(url), "http://localhost:%d/~core/ssb/#%s", s_port, identifier);
		tf_printf("Navigating to %s.\n", url);
		ViewController* view_controller = (ViewController*)self.window.rootViewController;
		[view_controller.web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithUTF8String:url]]]];
	}
	else
	{
		tf_printf("no search\n");
	}
	return NO;
}
@end

void tf_notify_message_added_ios(const char* identifier, const char* title, const char* content)
{
	tf_printf("indexing: identifier=%s title=%s content=%s\n", identifier, title, content);
	CSSearchableItemAttributeSet* attribute_set = [[CSSearchableItemAttributeSet alloc] initWithContentType:UTTypeText];
	attribute_set.title = [NSString stringWithUTF8String:content];
	attribute_set.contentDescription = [NSString stringWithUTF8String:title];
	CSSearchableItem* item = [[CSSearchableItem alloc] initWithUniqueIdentifier:[NSString stringWithUTF8String:identifier] domainIdentifier:@"com.unprompted.tildefriends.messages"
																   attributeSet:attribute_set];
	[[CSSearchableIndex defaultSearchableIndex] indexSearchableItems:@[ item ] completionHandler:^(NSError* _Nullable error) {
		if (error)
		{
			tf_printf("indexing error: %s.\n", [error.localizedDescription UTF8String]);
		}
		else
		{
			tf_printf("indexed successfully.\n");
		}
	}];
}

int main(int argc, char* argv[])
{
	NSFileManager* file_manager = [NSFileManager defaultManager];
	NSString* library_directory = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
	[file_manager changeCurrentDirectoryPath:library_directory];
	size_t path_length = strlen(argv[0]) - strlen(basename(argv[0]));

	size_t length = path_length + strlen("data.zip");
	char* zip_path = alloca(length + 1);
	snprintf(zip_path, length + 1, "%.*sdata.zip", (int)path_length, argv[0]);

	const char* temporary_directory = [NSTemporaryDirectory() UTF8String];
	length = strlen(temporary_directory) + strlen("port.txt") + 1;
	char* port_txt = alloca(length);
	snprintf(port_txt, length, "%sport.txt", temporary_directory);

	length = strlen("http_port=12345,out_http_port_file=") + strlen(port_txt) + 1;
	char* arguments = alloca(length);
	snprintf(arguments, length, "http_port=12345,out_http_port_file=%s", port_txt);
	tf_run_thread_start(zip_path, arguments);

	while (!s_port)
	{
		FILE* file = fopen(port_txt, "r");
		if (file)
		{
			char buffer[32] = { 0 };
			fread(buffer, sizeof(buffer), 1, file);
			s_port = atoi(buffer);
			fclose(file);
		}
		uv_sleep(100);
	}
	return UIApplicationMain(argc, argv, nil, NSStringFromClass(AppDelegate.class));
}
