package monitor

import (
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"net/mail"
	"net/smtp"
	"strings"

	"github.com/ethereum/go-ethereum/common"
)

const (
	// FINED notes the node is fined
	FINED uint = iota
	// INSUFFICIENT_TAN notes the node key with balance < 100 TAN
	INSUFFICIENT_TAN
	// INSUFFICIENT_ETH notes the node key with balance < threshold
	INSUFFICIENT_ETH
)

// Notifier is the interface for notifier interfaces.
type Notifier interface {
	notify(node, string, uint, string)
}

// Email is the email notifier object.
type Email struct {
	sender     string
	password   string
	smtpServer string
	ccList     string
	skipList   []string
}

// NewEmail is the Email constructor.
func NewEmail(sender, password, server, ccList string, skipList []string) Notifier {
	e := Email{
		sender:     sender,
		password:   password,
		smtpServer: server,
		ccList:     ccList,
		skipList:   skipList,
	}
	return &e
}

func (e *Email) notify(n node, network string, notifyType uint, threshold string) {
	var subj string
	var body string
	for _, skip := range e.skipList {
		if(skip == n.email) {
			return
		}
	}
	switch notifyType {
	case FINED:
		subj = "[Notification] Your Tangerine " + network + " Network Full Node is Fined"
		body = e.finedBody(n.name)
		go e.send(n.email, network, subj, body)
	case INSUFFICIENT_TAN:
		subj = "[Notification] Tangerine " + network + " Insufficient Balance in Node Key"
		body = e.insufficientBalanceBody(n.name, n.nodeKeyAddress)
		go e.send(n.email, network, subj, body)
	case INSUFFICIENT_ETH:
		subj = "[Notification] Tangerine " + network + " Insufficient ETH in Node Key"
		body = e.insufficientETHBalanceBody(n.name,
			n.nodeKeyAddress, threshold, network)
		go e.send(n.email, network, subj, body)
	}
}

func (e *Email) finedBody(name string) string {
	body := `
Dear Node Operator:

	Your full node "` + name + `" is fined.
	Please pay the fine, and check your node status.
	Also, please check https://tangerine-network.github.io/wiki/#/Rules-for-the-node-set?id=penalty for the penalty condition.

	Thanks for your cooperation.

-- Tangerine Network
`
	return body
}

func (e *Email) insufficientBalanceBody(name string, address common.Address) string {
	body := `
Dear Node Operator:

	There is no sufficient balacne in your full node "` + name + `" key address "` + address.Hex() + `".
	Please transfer some tokens to the node key address and keep the balance at least 200 TAN.

	Thanks for your cooperation.

-- Tangerine Network
`
	return body
}

func (e *Email) insufficientETHBalanceBody(
	name string,
	address common.Address,
	threshold string,
	network string) string {
	ethNet := "Rinkeby"
	if network == "Mainnet" {
		ethNet = "Mainnet"
	}
	body := `
Dear Node Operator:

	There is no sufficient ETH in your full node "` + name + `" key address "` + address.Hex() + `".
	Please transfer some ETH (` + ethNet + `) to the node key address and keep the balance at least ` + threshold + ` ETH.

	Thanks for your cooperation.

-- Tangerine Network
`
	return body
}

func (e *Email) send(toAddress, network, subj, body string) {
	from := mail.Address{Name: "Tangerine Notifier", Address: e.sender}
	to := mail.Address{Address: toAddress}
	// Setup headers
	headers := make(map[string]string)
	headers["From"] = from.String()
	headers["To"] = to.String()
	headers["Subject"] = subj
	if e.ccList != "" {
		headers["Cc"] = strings.TrimSuffix(e.ccList, "\n")
	}

	// Setup message
	message := ""
	for k, v := range headers {
		message += fmt.Sprintf("%s: %s\r\n", k, v)
	}
	message += "\r\n" + body

	// Connect to the SMTP Server
	servername := e.smtpServer + ":465"

	host, _, _ := net.SplitHostPort(servername)

	auth := smtp.PlainAuth("", e.sender, e.password, host)

	// TLS config
	tlsconfig := &tls.Config{
		InsecureSkipVerify: false,
		ServerName:         host,
	}

	// Here is the key, you need to call tls.Dial instead of smtp.Dial
	// for smtp servers running on 465 that require an ssl connection
	// from the very beginning (no starttls)
	conn, err := tls.Dial("tcp", servername, tlsconfig)
	if err != nil {
		log.Panic(err)
	}

	c, err := smtp.NewClient(conn, host)
	if err != nil {
		log.Panic(err)
	}

	// Auth
	if err = c.Auth(auth); err != nil {
		log.Panic(err)
	}

	// To && From
	if err = c.Mail(from.Address); err != nil {
		log.Panic(err)
	}

	if err = c.Rcpt(to.Address); err != nil {
		log.Panic(err)
	}

	if e.ccList != "" {
		cc := strings.Split(e.ccList, ",")
		for _, address := range cc {
			address = strings.TrimSuffix(address, "\n")
			if err = c.Rcpt(address); err != nil {
				log.Println("add cc fail", address, err)
			}
		}
	}

	// Data
	w, err := c.Data()
	if err != nil {
		log.Panic(err)
	}

	_, err = w.Write([]byte(message))
	if err != nil {
		log.Panic(err)
	}

	err = w.Close()
	if err != nil {
		log.Panic(err)
	}

	c.Quit()
}