package contact

import (
	"crypto/tls"
	"fmt"
	"gitea.inhome.blapointe.com/local/system/sysconf"
	"log"
	"net"
	"net/smtp"
	"regexp"
	"strings"
	"time"

	pop3 "github.com/taknb2nch/go-pop3"
)

// Emailer allows sending of emails (or better
// texts with the right address) with TLS.
type Emailer interface {
	Send(string, string) error
	Check() []Email
}

type emailer struct {
	to         string
	from       string
	smtpServer string
	pop3Server string
	pwd        string
}

type Email struct {
	To      string
	From    string
	Date    time.Time
	Subject string
	Body    string
}

func (e Email) String() string {
	return fmt.Sprintf("To: %q\nFrom: %q\nSubj: %q\nDate: %q\nBody: %q", e.To, e.From, e.Subject, e.Date.String(), e.Body)
}

// NewEmailer returns a emailer capable of Send()ing
// To the given address.
func NewEmailer(To string) Emailer {
	config := sysconf.Get("email")
	return &emailer{
		smtpServer: config.IP,
		pop3Server: config.IP2,
		to:         To,
		from:       config.Log,
		pwd:        config.Port,
	}
}

// Send sends an email To the configured address with
// the given Subject and message.
func (e *emailer) Send(subj, msg string) error {
	// Setup headers
	headers := make(map[string]string)
	headers["From"] = e.from
	headers["To"] = e.to
	headers["Subject"] = subj
	Body := ""
	for k, v := range headers {
		Body += fmt.Sprintf("%s: %s\r\n", k, v)
	}
	Body += "\r\n" + msg

	// Connect To the SMTP Server
	c := e.newSmtpClient()
	defer c.Quit()

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

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

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

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

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

	return nil
}

func (e *emailer) newSmtpClient() *smtp.Client {
	host, _, _ := net.SplitHostPort(e.smtpServer)
	auth := smtp.PlainAuth("", e.from, e.pwd, host)
	conn := e.getConn(e.smtpServer)

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

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

	return c
}

func (e *emailer) getConn(remote string) net.Conn {
	host, _, _ := net.SplitHostPort(remote)
	tlsconfig := &tls.Config{
		InsecureSkipVerify: true,
		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", remote, tlsconfig)
	if err != nil {
		log.Panic(err)
	}
	return conn
}

func (e *emailer) newPop3Client() *pop3.Client {
	conn := e.getConn(e.pop3Server)
	client, err := pop3.NewClient(conn)

	if err != nil {
		log.Fatalf("Dial Error: %v\n", err)
	}

	if err = client.User(e.from); err != nil {
		log.Panicf("User Error: %v\n", err)
	}

	if err = client.Pass(e.pwd); err != nil {
		log.Panicf("Pass Error: %v\n", err)
	}

	return client
}

func (e *emailer) Check() []Email {
	client := e.newPop3Client()
	defer func() {
		client.Quit()
		client.Close()
	}()

	var count int
	var err error

	if count, _, err = client.Stat(); err != nil {
		log.Printf("Stat Error: %v\n", err)
		return nil
	}

	var content string
	emails := make([]Email, count)

	for i := 1; i <= count; i++ {
		if content, err = client.Retr(i); err != nil {
			log.Printf("Retr Error: %v\n", err)
			return nil
		}

		emails[i-1] = parseEmail(content)
	}
	return emails
}

func (e *emailer) pop3all() []Email {
	conn := e.getConn(e.pop3Server)
	client, err := pop3.NewClient(conn)

	if err != nil {
		log.Fatalf("Dial Error: %v\n", err)
	}

	defer func() {
		client.Quit()
		client.Close()
	}()

	if err = client.User(e.from); err != nil {
		log.Printf("User Error: %v\n", err)
		return nil
	}

	if err = client.Pass(e.pwd); err != nil {
		log.Printf("Pass Error: %v\n", err)
		return nil
	}

	var count int
	var size uint64

	if count, size, err = client.Stat(); err != nil {
		log.Printf("Stat Error: %v\n", err)
		return nil
	}

	log.Printf("Count: %d, Size: %d\n", count, size)

	if count, size, err = client.List(6); err != nil {
		log.Printf("List Error: %v\n", err)
		return nil
	}

	log.Printf("Number: %d, Size: %d\n", count, size)

	var mis []pop3.MessageInfo

	if mis, err = client.ListAll(); err != nil {
		log.Printf("LAll Error: %v\n", err)
		return nil
	}

	for _, mi := range mis {
		log.Printf("Number: %d, Size: %d\n", mi.Number, mi.Size)
	}

	var number int
	var uid string

	if number, uid, err = client.Uidl(6); err != nil {
		log.Printf("Uidl Error: %v\n", err)
		return nil
	}

	log.Printf("Number: %d, Uid: %s\n", number, uid)

	if mis, err = client.UidlAll(); err != nil {
		log.Printf("UAll Error: %v\n", err)
		return nil
	}

	for _, mi := range mis {
		log.Printf("Number: %d, Uid: %s\n", mi.Number, mi.Uid)
	}

	var content string

	if content, err = client.Retr(8); err != nil {
		log.Printf("Retr Error: %v\n", err)
		return nil
	}

	log.Printf("Content:\n%s\n", content)

	/*
		if err = client.Dele(6); err != nil {
			log.Printf("Dele Error: %v\n", err)
			return nil
		}
	*/

	if err = client.Noop(); err != nil {
		log.Printf("Noop Error: %v\n", err)
		return nil
	}

	if err = client.Rset(); err != nil {
		log.Printf("Rset Error: %v\n", err)
		return nil
	}

	return nil
}

/*
type Email struct {
	To      string
	From    string
	Date    time.Time
	Subject string
	Body    string
}
*/
func parseEmail(content string) Email {
	To := regexp.MustCompile("\n[Tt]o: .*\n").FindString(content)
	To = strings.TrimSpace(To)
	To = strings.Trim(To, "TTo")
	To = strings.Trim(To, ": {}<>")

	Subject := regexp.MustCompile("\n[Ss]ubject: .*\n").FindString(content)
	Subject = strings.TrimSpace(Subject)
	Subject = strings.Trim(Subject, "SSubject")
	Subject = strings.Trim(Subject, ": ")

	when := regexp.MustCompile("[Dd]ate: .*\n").FindString(content)
	when = strings.Trim(when, "DDate")
	when = strings.Trim(when, ":")
	when = strings.TrimSpace(when)
	Date, err := time.Parse("Mon, 2 Jan 2006 15:04:05 -0700 (MST)", when)
	if err != nil {
		log.Println(err)
	}

	parseFrom := regexp.MustCompile("[^ ]*@[^ ]*")
	From := regexp.MustCompile("\n[Ff]rom: .*\n").FindString(content)
	From = strings.TrimSpace(From)
	From = parseFrom.FindString(From)
	From = strings.Trim(From, "<>{}")

	Body := ""
	Bodyarr := strings.Split(content, "\n")
	Body = Bodyarr[len(Bodyarr)-1]

	return Email{
		To:      To,
		From:    From,
		Date:    Date,
		Subject: Subject,
		Body:    Body,
	}
}
