package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"time"

	"github.com/emersion/go-imap"
	idle "github.com/emersion/go-imap-idle"
	"github.com/emersion/go-imap/client"
	"github.com/emersion/go-message/mail"
)

func main() {
	host := os.Getenv("IMAP_HOST")
	username := os.Getenv("IMAP_USERNAME")
	password := os.Getenv("IMAP_PASSWORD")
	folder := os.Getenv("IMAP_FOLDER")
	if folder == "" {
		folder = "INBOX"
	}

	if host == "" || username == "" || password == "" {
		log.Fatal("Required environment variables are not set")
	}

	log.Println("Connecting to server...")
	c, err := client.DialTLS(host, nil)
	if err != nil {
		log.Fatalf("Failed to connect to IMAP server: %v", err)
	}
	defer c.Logout()

	if err := c.Login(username, password); err != nil {
		log.Fatalf("Failed to log in: %v", err)
	}

	log.Println("Logged in and authenticated")

	// Ensure the selected folder is continuously monitored
	for {
		if err := monitorFolder(c, folder); err != nil {
			log.Printf("Error monitoring folder %s: %v", folder, err)
			log.Println("Attempting to reconnect in 30 seconds...")
			time.Sleep(30 * time.Second)
			continue
		}
	}
}

func monitorFolder(c *client.Client, folder string) error {
	_, err := c.Select(folder, false)
	if err != nil {
		return fmt.Errorf("failed to select '%s': %w", folder, err)
	}

	log.Printf("Monitoring folder: %s", folder)
	idleClient := idle.NewClient(c)
	done := make(chan error, 1)

	go func() {
		done <- idleClient.Idle(nil)
	}()

	for {
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("idle error: %w", err)
			}
			return nil
		case <-time.After(29 * time.Minute): // Re-initiate IDLE approximately every 29 minutes
			if err := idleClient.Terminate(); err != nil {
				log.Printf("Error terminating IDLE: %v", err)
			}
			return fmt.Errorf("reinitializing IDLE")
		}
	}
}

func processNewMessages(c *client.Client, mbox *imap.MailboxStatus) {
	seqset := new(imap.SeqSet)
	seqset.AddRange(mbox.Messages, mbox.Messages)

	messages := make(chan *imap.Message, 10)
	go func() {
		if err := c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope, imap.FetchBody}, messages); err != nil {
			log.Fatalf("Failed to fetch new messages: %v", err)
		}
	}()

	for msg := range messages {
		processMessage(msg)
	}
}

// processMessage processes a single message, extracting data and saving to JSON
func processMessage(msg *imap.Message) {
	if msg == nil {
		log.Println("No message to process")
		return
	}

	body := msg.GetBody(&imap.BodySectionName{})
	if body == nil {
		log.Println("No body fetched for this message")
		return
	}

	r, err := mail.CreateReader(body)
	if err != nil {
		log.Fatalf("Failed to create mail reader: %v", err)
	}

	var emailBody strings.Builder
	for {
		p, err := r.NextPart()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatalf("Failed to read part: %v", err)
		}

		switch h := p.Header.(type) {
		case *mail.InlineHeader:
			_, params, _ := h.ContentType()
			if params["charset"] != "" {
				_, err = io.Copy(&emailBody, p.Body)
				if err != nil {
					log.Fatalf("Failed to read body: %v", err)
				}
			}
		}
	}

	emailData := map[string]interface{}{
		"subject": msg.Envelope.Subject,
		"from":    msg.Envelope.From,
		"to":      msg.Envelope.To,
		"date":    msg.Envelope.Date,
		"body":    emailBody.String(),
	}

	emailJSON, err := json.MarshalIndent(emailData, "", "  ")
	if err != nil {
		log.Fatalf("Failed to marshal JSON: %v", err)
	}

	timestamp := time.Now().Format("20060102-150405")
	fileName := fmt.Sprintf("email-%s.json", timestamp)
	err = os.WriteFile(fileName, emailJSON, 0644)
	if err != nil {
		log.Fatalf("Failed to write file %s: %v", fileName, err)
	}
	log.Printf("Saved %s", fileName)
}
