package gui

// startupGui.go implements the app startup pages

import "fyne.io/fyne/v2"
import "fyne.io/fyne/v2/app"
import "fyne.io/fyne/v2/widget"
import "fyne.io/fyne/v2/container"
import "fyne.io/fyne/v2/canvas"
import "fyne.io/fyne/v2/layout"
import "fyne.io/fyne/v2/dialog"
import "fyne.io/fyne/v2/theme"

import "seekia/internal/appMemory"
import "seekia/internal/badgerDatabase"
import "seekia/internal/localFilesystem"
import "seekia/internal/helpers"
import "seekia/internal/appUsers"
import "seekia/internal/mySeedPhrases"
import "seekia/internal/globalSettings"
import "seekia/internal/translation"
import "seekia/internal/imageFiles"
import "seekia/internal/imagery"
import "seekia/internal/backgroundJobs"

import "time"
import "errors"
import "slices"

func StartGui(){

	app := app.New()

	window := app.NewWindow("Seekia")

	windowSize := fyne.NewSize(600, 600)
	window.Resize(windowSize)

	window.CenterOnScreen()

	err := localFilesystem.InitializeAppDatastores()
	if (err != nil){

		getThemeObject := func()fyne.Theme{

			themeObject, err := getCustomFyneTheme("Light")
			if (err != nil){
				return theme.LightTheme()
			}

			return themeObject
		}
	
		themeObject := getThemeObject()

		app.Settings().SetTheme(themeObject)

		errorToShow := errors.New("Seekia cannot access the filesystem: " + err.Error())

		setErrorEncounteredPage_NoNavBar(window, errorToShow, false, nil)

		window.ShowAndRun()
		return
	}

	getAppTheme := func()(fyne.Theme, error){

		getAppThemeName := func()(string, error){

			exists, currentAppTheme, err := globalSettings.GetSetting("AppTheme")
			if (err != nil){
				return "", errors.New("Seekia cannot access the filesystem: " + err.Error())	
			}
			if (exists == false){
				return "Light", nil
			}
			return currentAppTheme, nil
		}

		appThemeName, err := getAppThemeName()
		if (err != nil) { return nil, err }

		themeObject, err := getCustomFyneTheme(appThemeName)
		if (err != nil){ return nil, err }
	
		return themeObject, nil	
	}

	appTheme, err := getAppTheme()
	if (err != nil){

		getThemeObject := func()fyne.Theme{

			themeObject, err := getCustomFyneTheme("Light")
			if (err != nil){
				return theme.LightTheme()
			}

			return themeObject
		}

		themeObject := getThemeObject()

		app.Settings().SetTheme(themeObject)

		setErrorEncounteredPage_NoNavBar(window, err, false, nil)

		window.ShowAndRun()
		return
	}

	app.Settings().SetTheme(appTheme)

	window.SetCloseIntercept(func(){

			appMemory.SetMemoryEntry("CurrentViewedPage", "Shutdown")

			description1 := getBoldLabelCentered("Shutting Down...")

			progressBar := getWidgetCentered(widget.NewProgressBarInfinite())

			description2 := getItalicLabelCentered("Seekia is shutting down background tasks.")

			emptyLabel := widget.NewLabel("")			

			forceCloseButton := getWidgetCentered(widget.NewButtonWithIcon("Force Close", theme.CancelIcon(), func(){
					window.Close()
				}))

			//TODO: Show the number of background tasks we are waiting to stop, along with their names

			page := container.NewVBox(layout.NewSpacer(), description1, progressBar, description2, emptyLabel, forceCloseButton, layout.NewSpacer())

			stopJobsAndCloseWindowFunction := func(){

				//TODO: Remove this time.Sleep once we actually have background tasks to close
				time.Sleep(time.Second)

				backgroundJobs.StopBackgroundJobs()

				badgerDatabase.StopDatabase()

				//TODO: Add peer node, peer server, manual broadcasts, manual downloads 
				// We should also use goroutines to start the stop function for all background tasks
				// This way they can all gracefully shutdown simultaneously while we wait for them to stop

				fyne.Do(window.Close)
			}

			window.SetContent(page)

			go stopJobsAndCloseWindowFunction()
		})

	//TODO: On first startup, first show Choose Language page.
	// Then show page describing legal risks of Seekia, including information about countries where Tor is illegal.
	// It should not be a generic warning that users will not read.
	// Users should actually stop if Seekia use is illegal and puts them at risk of prosecution.

	setChooseAppUserPage(window)

	window.ShowAndRun()
}


func setChooseAppUserPage(window fyne.Window){

	currentPage := func(){setChooseAppUserPage(window)}

	logoFileBytes := imageFiles.PNG_SeekiaLogo
	logoGoImage, err := imagery.ConvertPNGImageFileBytesToGolangImage(logoFileBytes)
	if (err != nil){
		setErrorEncounteredPage_NoNavBar(window, err, true, currentPage)
		return
	}

	seekiaLogo := canvas.NewImageFromImage(logoGoImage)
	seekiaLogo.FillMode = canvas.ImageFillContain
	logoSize := getCustomFyneSize(30)
	seekiaLogo.SetMinSize(logoSize)

	seekiaDescriptionLabel := getItalicLabelCentered("Be genetics aware.")

	selectLanguageTitle := getBoldLabel("Language:")	

	currentLanguage := translation.GetMyLanguage()
	selectLanguageButton := getWidgetCentered(widget.NewButton(currentLanguage, func(){

			setSelectLanguagePage(window, false, currentPage)
		}))

	selectLanguageRow := container.NewHBox(layout.NewSpacer(), selectLanguageTitle, selectLanguageButton, layout.NewSpacer()) 

	spacerA := widget.NewLabel("")
	spacerB := widget.NewLabel("")

	page := container.NewVBox(spacerA, spacerB, seekiaLogo, seekiaDescriptionLabel, widget.NewSeparator(), selectLanguageRow, widget.NewSeparator())

	allUsersList, err := appUsers.GetAppUsersList()
	if (err != nil){
		setErrorEncounteredPage_NoNavBar(window, err, true, currentPage)
		return
	}

	if (len(allUsersList) != 0){

		selectUserLabel := getBoldLabelCentered("Select User:")
		page.Add(selectUserLabel)

		userButtonsGrid := container.NewGridWithColumns(1)

		for _, userName := range allUsersList{

			selectUserButton := widget.NewButtonWithIcon(userName, theme.AccountIcon(), func(){

					setLoadingScreen(window, translate("Starting Up"), translate("Loading..."))

					err := appUsers.SignInToAppUser(userName, true)
					if (err != nil){
						setErrorEncounteredPage_NoNavBar(window, err, true, currentPage)
						return
					}

					setHomePage(window)
				})

			selectUserButton.Importance = widget.HighImportance

			userButtonsGrid.Add(selectUserButton)			
		}

		userButtonsGridCentered := getContainerCentered(userButtonsGrid)
		page.Add(userButtonsGridCentered)

		page.Add(widget.NewSeparator())
	}

	createUserButton := widget.NewButtonWithIcon("Create User", theme.ContentAddIcon(), func(){
			setCreateAppUserPage(window, currentPage, currentPage)
		})

	if (len(allUsersList) == 0){

		createUserButtonCentered := getWidgetCentered(createUserButton)

		page.Add(createUserButtonCentered)
	} else {

		renameUserButton := widget.NewButtonWithIcon("Rename User", theme.DocumentCreateIcon(), func(){
				setRenameAppUserPage(window, currentPage, currentPage)
			})

		deleteUserButton := widget.NewButtonWithIcon("Delete User", theme.DeleteIcon(), func(){
				setDeleteAppUserPage(window, currentPage, currentPage)
			})

		userActionButtonsGrid := getContainerCentered(container.NewGridWithColumns(1, createUserButton, renameUserButton, deleteUserButton))
	
		page.Add(userActionButtonsGrid)
		page.Add(widget.NewSeparator())
	}

	page.Add(layout.NewSpacer())
	
	pageScrollable := container.NewVScroll(page)	

	window.SetContent(pageScrollable)
}


func setCreateAppUserPage(window fyne.Window, previousPage func(), afterCreatePage func()){

	currentPage := func(){setCreateAppUserPage(window, previousPage, afterCreatePage)}

	title := getPageTitleCentered("Create User")
	
	backButton := getBackButtonCentered(previousPage)

	userHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
			setAppUserExplainerPage(window, currentPage)
		})
	description1 := getBoldLabel("Create a Seekia user.")

	description1Row := container.NewHBox(layout.NewSpacer(), description1, userHelpButton, layout.NewSpacer())
	
	description2 := widget.NewLabel("This name will not be shared publicly.")

	nameEntry := widget.NewEntry()
	nameEntry.PlaceHolder = "Enter name..."

	nameEntryBoxed := getWidgetBoxed(nameEntry)	

	description2WithEntry := getContainerCentered(container.NewGridWithColumns(1, description2, nameEntryBoxed))

	createUserButton := getWidgetCentered(widget.NewButtonWithIcon("Create", theme.ConfirmIcon(), func(){

			newUserName := nameEntry.Text
	
			if (newUserName == ""){
				dialogTitle := translate("User Name Is Empty")
				dialogMessageA := getLabelCentered(translate("The user name is empty."))
				dialogMessageB := getLabelCentered(translate("Enter a name."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			if (len(newUserName) > 30){
				dialogTitle := translate("User Name Is Too Long")
				dialogMessageA := getLabelCentered(translate("The user name is too long."))
				dialogMessageB := getLabelCentered(translate("It cannot exceed 30 characters."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			isAllowed := appUsers.VerifyAppUserNameCharactersAreAllowed(newUserName)
			if (isAllowed == false){
				dialogTitle := translate("User Name Is Not Allowed")
				dialogMessageA := getLabelCentered(translate("The user name is not allowed."))
				dialogMessageB := getLabelCentered(translate("It must contain only numbers and letters."))
				dialogMessageC := getLabelCentered(translate("It cannot contain any spaces."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			alreadyExists, err := appUsers.CreateAppUser(newUserName)
			if (err != nil) {
				setErrorEncounteredPage_NoNavBar(window, err, true, currentPage)
				return
			}
			if (alreadyExists == true){
				dialogTitle := translate("User Name Is A Duplicate.")
				dialogMessageA := getLabelCentered(translate("You already have a user with this name."))
				dialogMessageB := getLabelCentered(translate("Enter a unique name."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			afterCreatePage()
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description1Row, description2WithEntry, createUserButton)

	window.SetContent(page)
}

func setAppUserExplainerPage(window fyne.Window, previousPage func()){

	title := getPageTitleCentered("Help - App User")

	backButton := getBackButtonCentered(previousPage)

	description1 := getBoldLabelCentered("To use Seekia, you must create an app user.")
	description2 := getLabelCentered("Each app user has its own data folder.")
	description3 := getLabelCentered("Each user can create their own Mate, Host, and Moderator identity.")
	description4 := getLabelCentered("This allows you to create multiple identities and switch between them.")
	description5 := getLabelCentered("For example, you could operate 2 moderator identities and switch between them.")
	description6 := getLabelCentered("Most users will only need to create 1 user.")
	description7 := getLabelCentered("Your user names are never uploaded or shared anywhere.")
	description8 := getLabelCentered("You must export each user's data when transferring to a new device.")

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8)

	window.SetContent(page)
}

func setRenameAppUserPage(window fyne.Window, previousPage func(), afterRenamePage func()){

	currentPage := func(){setRenameAppUserPage(window, previousPage, afterRenamePage)}

	title := getPageTitleCentered("Rename User")

	backButton := getBackButtonCentered(previousPage)

	allUsersList, err := appUsers.GetAppUsersList()
	if (err != nil){
		setErrorEncounteredPage_NoNavBar(window, err, true, previousPage)
		return
	}

	if (len(allUsersList) == 0){
		setErrorEncounteredPage_NoNavBar(window, errors.New("setRenameAppUserPage called when no users exist."), true, previousPage)
		return
	}

	selectUserLabel := getBoldLabelCentered("Select User:")
	
	userSelector := widget.NewSelect(allUsersList, nil)
	userSelector.PlaceHolder = "Select user..."

	userSelectorCentered := getWidgetCentered(userSelector)

	enterNewNameLabel := getBoldLabel("Enter New Name:")

	newNameEntry := widget.NewEntry()
	newNameEntry.SetPlaceHolder("Enter new name...")

	newNameEntryBoxed := getWidgetBoxed(newNameEntry)

	newNameEntryWithLabel := getContainerCentered(container.NewGridWithColumns(1, enterNewNameLabel, newNameEntryBoxed))

	renameButton := getWidgetCentered(widget.NewButtonWithIcon("Rename", theme.ConfirmIcon(), func(){

			currentUserNameSelectedIndex := userSelector.SelectedIndex()
			if (currentUserNameSelectedIndex == -1){
				dialogTitle := translate("No User Selected")
				dialogMessageA := getLabelCentered(translate("No user is selected."))
				dialogMessageB := getLabelCentered(translate("Select the user you want to rename."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			currentUserName := userSelector.Selected

			newUserName := newNameEntry.Text

			if (newUserName == ""){
				dialogTitle := translate("User Name Is Empty")
				dialogMessageA := getLabelCentered(translate("The user name is empty."))
				dialogMessageB := getLabelCentered(translate("Enter a name."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			if (len(newUserName) > 30){
				dialogTitle := translate("User Name Is Too Long")
				dialogMessageA := getLabelCentered(translate("The user name is too long."))
				dialogMessageB := getLabelCentered(translate("It cannot exceed 30 characters."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			isAllowed := appUsers.VerifyAppUserNameCharactersAreAllowed(newUserName)
			if (isAllowed == false){
				dialogTitle := translate("User Name Is Not Allowed")
				dialogMessageA := getLabelCentered(translate("The user name is not allowed."))
				dialogMessageB := getLabelCentered(translate("It must contain only numbers and letters."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			alreadyExists := slices.Contains(allUsersList, newUserName)
			if (alreadyExists == true){
				dialogTitle := translate("User Name Is A Duplicate.")
				dialogMessageA := getLabelCentered(translate("You already have a user with this name."))
				dialogMessageB := getLabelCentered(translate("Enter a unique name."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			setLoadingScreen(window, translate("Rename User"), translate("Renaming User..."))

			userFound, err := appUsers.RenameAppUser(currentUserName, newUserName)
			if (err != nil){
				setErrorEncounteredPage_NoNavBar(window, err, true, currentPage)
				return
			}
			if (userFound == false){
				setErrorEncounteredPage_NoNavBar(window, errors.New("App user not found after being found already."), true, currentPage)
				return
			}

			afterRenamePage()
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), selectUserLabel, userSelectorCentered, newNameEntryWithLabel, renameButton)

	window.SetContent(page)
}


func setDeleteAppUserPage(window fyne.Window, previousPage func(), afterDeletePage func()){

	currentPage := func(){setDeleteAppUserPage(window, previousPage, afterDeletePage)}

	title := getPageTitleCentered("Delete User")

	backButton := getBackButtonCentered(previousPage)

	description := getBoldLabelCentered("Choose the user to delete.")

	allUsersList, err := appUsers.GetAppUsersList()
	if (err != nil){
		setErrorEncounteredPage_NoNavBar(window, err, true, previousPage)
		return
	}

	if (len(allUsersList) == 0){
		setErrorEncounteredPage_NoNavBar(window, errors.New("setDeleteAppUserPage called when no users exist."), true, previousPage)
		return
	}

	userButtonsGrid := container.NewGridWithColumns(1)

	for _, userName := range allUsersList{

		selectUserButton := widget.NewButtonWithIcon(userName, theme.AccountIcon(), func(){
				setConfirmDeleteAppUserPage(window, userName, currentPage, afterDeletePage)
			})

		userButtonsGrid.Add(selectUserButton)			
	}

	userButtonsGridCentered := getContainerCentered(userButtonsGrid)	

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), userButtonsGridCentered)

	window.SetContent(page)
}

func setConfirmDeleteAppUserPage(window fyne.Window, userName string, previousPage func(), afterDeletePage func()){

	setLoadingScreen(window, translate("Delete User"), translate("Loading Delete User Page..."))

	currentPage := func(){setConfirmDeleteAppUserPage(window, userName, previousPage, afterDeletePage)}

	title := getPageTitleCentered("Delete User")

	backButton := getBackButtonCentered(previousPage)
	
	// We sign in to the user to see if there are any user identities 

	getNumberOfUserIdentities := func()(int, error){

		err := appUsers.SignInToAppUser(userName, false)
		if (err != nil){ return 0, err }

		numberOfIdentities := 0

		mateIdentityExists, _, err := mySeedPhrases.GetMySeedPhrase("Mate")
		if (err != nil) { return 0, err }
		if (mateIdentityExists == true){
			numberOfIdentities += 1
		}

		hostIdentityExists, _, err := mySeedPhrases.GetMySeedPhrase("Host")
		if (err != nil) { return 0, err }
		if (hostIdentityExists == true){
			numberOfIdentities += 1
		}

		moderatorIdentityExists, _, err := mySeedPhrases.GetMySeedPhrase("Moderator")
		if (err != nil) { return 0, err }
		if (moderatorIdentityExists == true){
			numberOfIdentities += 1
		}

		err = appUsers.SignOutOfAppUser()
		if (err != nil){ return 0, err }

		return numberOfIdentities, nil
	}

	numberOfIdentities, err := getNumberOfUserIdentities()
	if (err != nil) {
		setErrorEncounteredPage_NoNavBar(window, err, true, previousPage)
		return
	}
	if (numberOfIdentities != 0){

		getNumberOfIdentitiesDescription := func()string{
			if (numberOfIdentities == 1){
				return "This user has 1 identity."
			}

			numberOfIdentitiesString := helpers.ConvertIntToString(numberOfIdentities)

			return "This user has " + numberOfIdentitiesString + " identities."
		}

		numberOfIdentitiesDescription := getNumberOfIdentitiesDescription()

		description1 := getBoldLabelCentered(numberOfIdentitiesDescription)
		description2 := getLabelCentered("You must delete all user identities before deleting the user.")
		description3 := getLabelCentered("Delete your identities on the Settings - My Data - Delete Identity page.")

		page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3)
		window.SetContent(page)
		return
	}

	description1 := getBoldLabelCentered("Are you sure you want to delete this user?")
	
	nameLabel := widget.NewLabel("Name:")
	userNameLabel := getBoldLabel(userName)

	userNameRow := container.NewHBox(layout.NewSpacer(), nameLabel, userNameLabel, layout.NewSpacer())

	description2 := getLabelCentered("This will delete all of your user data.")
	description3 := getLabelCentered("This includes your desires, genetic analyses, genomes, and settings.")
	description4 := getLabelCentered("Export your data on the Settings - My Data - Export Data page to retain your data.")

	deleteButton := getWidgetCentered(widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){

			setLoadingScreen(window, translate("Delete User"), translate("Deleting User..."))

			userExists, err := appUsers.DeleteAppUser(userName)
			if (err != nil){
				setErrorEncounteredPage_NoNavBar(window, err, true, currentPage)
				return
			}
			if (userExists == false){
				setErrorEncounteredPage_NoNavBar(window, errors.New("setConfirmDeleteAppUserPage called when user does not exist."), true, currentPage)
				return
			}
			afterDeletePage()
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, userNameRow, description2, description3, description4, deleteButton)
	
	window.SetContent(page)
}



