// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge.  If not, see <https://www.gnu.org/licenses/>.

package cli

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/ProtonMail/proton-bridge/internal/bridge"
	"github.com/ProtonMail/proton-bridge/internal/preferences"
	"github.com/ProtonMail/proton-bridge/pkg/connection"
	"github.com/ProtonMail/proton-bridge/pkg/ports"
	"github.com/abiosoft/ishell"
)

var (
	currentPort = "" //nolint[gochecknoglobals]
)

func (f *frontendCLI) restart(c *ishell.Context) {
	if f.yesNoQuestion("Are you sure you want to restart the Bridge") {
		f.Println("Restarting Bridge...")
		f.appRestart = true
		f.Stop()
	}
}

func (f *frontendCLI) checkInternetConnection(c *ishell.Context) {
	if connection.CheckInternetConnection() == nil {
		f.Println("Internet connection is available.")
	} else {
		f.Println("Can not contact server please check you internet connection.")
	}
}

func (f *frontendCLI) printLogDir(c *ishell.Context) {
	f.Println("Log files are stored in\n\n ", f.config.GetLogDir())
}

func (f *frontendCLI) printManual(c *ishell.Context) {
	f.Println("More instructions about the Bridge can be found at\n\n  https://protonmail.com/bridge")
}

func (f *frontendCLI) deleteCache(c *ishell.Context) {
	f.ShowPrompt(false)
	defer f.ShowPrompt(true)

	if !f.yesNoQuestion("Do you really want to remove all stored preferences") {
		return
	}
	if err := f.bridge.ClearData(); err != nil {
		f.printAndLogError("Cache clear failed: ", err.Error())
		return
	}
	f.Println("Cached cleared, restarting bridge")
	// Clearing data removes everything (db, preferences, ...)
	// so everything has to be stopped and started again.
	f.appRestart = true
	f.Stop()
}

func (f *frontendCLI) changeSMTPSecurity(c *ishell.Context) {
	f.ShowPrompt(false)
	defer f.ShowPrompt(true)

	isSSL := f.preferences.GetBool(preferences.SMTPSSLKey)
	newSecurity := "SSL"
	if isSSL {
		newSecurity = "STARTTLS"
	}

	msg := fmt.Sprintf("Are you sure you want to change SMTP setting to %q and restart the Bridge", newSecurity)

	if f.yesNoQuestion(msg) {
		f.preferences.SetBool(preferences.SMTPSSLKey, !isSSL)
		f.Println("Restarting Bridge...")
		f.appRestart = true
		f.Stop()
	}
}

func (f *frontendCLI) changePort(c *ishell.Context) {
	f.ShowPrompt(false)
	defer f.ShowPrompt(true)

	currentPort = f.preferences.Get(preferences.IMAPPortKey)
	newIMAPPort := f.readStringInAttempts("Set IMAP port (current "+currentPort+")", c.ReadLine, f.isPortFree)
	if newIMAPPort == "" {
		newIMAPPort = currentPort
	}
	imapPortChanged := newIMAPPort != currentPort

	currentPort = f.preferences.Get(preferences.SMTPPortKey)
	newSMTPPort := f.readStringInAttempts("Set SMTP port (current "+currentPort+")", c.ReadLine, f.isPortFree)
	if newSMTPPort == "" {
		newSMTPPort = currentPort
	}
	smtpPortChanged := newSMTPPort != currentPort

	if newIMAPPort == newSMTPPort {
		f.Println("SMTP and IMAP ports must be different!")
		return
	}

	if imapPortChanged || smtpPortChanged {
		f.Println("Saving values IMAP:", newIMAPPort, "SMTP:", newSMTPPort)
		f.preferences.Set(preferences.IMAPPortKey, newIMAPPort)
		f.preferences.Set(preferences.SMTPPortKey, newSMTPPort)
		f.Println("Restarting Bridge...")
		f.appRestart = true
		f.Stop()
	} else {
		f.Println("Nothing changed")
	}
}

func (f *frontendCLI) toggleAllowProxy(c *ishell.Context) {
	if f.preferences.GetBool(preferences.AllowProxyKey) {
		f.Println("Bridge is currently set to use alternative routing to connect to Proton if it is being blocked.")
		if f.yesNoQuestion("Are you sure you want to stop bridge from doing this") {
			f.preferences.SetBool(preferences.AllowProxyKey, false)
			bridge.DisallowDoH()
		}
	} else {
		f.Println("Bridge is currently set to NOT use alternative routing to connect to Proton if it is being blocked.")
		if f.yesNoQuestion("Are you sure you want to allow bridge to do this") {
			f.preferences.SetBool(preferences.AllowProxyKey, true)
			bridge.AllowDoH()
		}
	}
}

func (f *frontendCLI) isPortFree(port string) bool {
	port = strings.Replace(port, ":", "", -1)
	if port == "" || port == currentPort {
		return true
	}
	number, err := strconv.Atoi(port)
	if err != nil || number < 0 || number > 65535 {
		f.Println("Input", port, "is not a valid port number.")
		return false
	}
	if !ports.IsPortFree(number) {
		f.Println("Port", number, "is occupied by another process.")
		return false
	}
	return true
}
