package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"mail2couch/config"
	"mail2couch/couch"
	"mail2couch/mail"
)

func main() {
	args := config.ParseCommandLine()

	cfg, err := config.LoadConfigWithDiscovery(args)
	if err != nil {
		log.Fatalf("Failed to load configuration: %v", err)
	}

	// Initialize CouchDB client
	couchClient, err := couch.NewClient(&cfg.CouchDb)
	if err != nil {
		log.Fatalf("Failed to create CouchDB client: %v", err)
	}

	fmt.Printf("Found %d mail source(s) to process.\n", len(cfg.MailSources))
	for _, source := range cfg.MailSources {
		if !source.Enabled {
			continue
		}

		// Generate per-account database name
		dbName := couch.GenerateAccountDBName(source.Name, source.User)

		// Ensure the account-specific database exists
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		err = couchClient.EnsureDB(ctx, dbName)
		cancel()

		if err != nil {
			log.Printf("Could not ensure CouchDB database '%s' exists (is it running?): %v", dbName, err)
			continue
		} else {
			fmt.Printf("CouchDB database '%s' is ready for account: %s\n", dbName, source.Name)
		}

		fmt.Printf(" - Processing source: %s\n", source.Name)
		if source.Protocol == "imap" {
			err := processImapSource(&source, couchClient, dbName, args.MaxMessages)
			if err != nil {
				log.Printf("   ERROR: Failed to process IMAP source %s: %v", source.Name, err)
			}
		}
	}
}

func processImapSource(source *config.MailSource, couchClient *couch.Client, dbName string, maxMessages int) error {
	fmt.Printf("   Connecting to IMAP server: %s:%d\n", source.Host, source.Port)
	imapClient, err := mail.NewImapClient(source)
	if err != nil {
		return fmt.Errorf("failed to connect to IMAP server: %w", err)
	}
	defer imapClient.Logout()

	fmt.Println("   IMAP connection successful.")

	// Use IMAP LIST with patterns for server-side filtering
	mailboxes, err := imapClient.ListFilteredMailboxes(&source.FolderFilter)
	if err != nil {
		return fmt.Errorf("failed to list filtered mailboxes: %w", err)
	}

	fmt.Printf("   Found %d matching mailboxes after filtering.\n", len(mailboxes))

	// Parse the since date from config if provided (fallback for first sync)
	var configSinceDate *time.Time
	if source.MessageFilter.Since != "" {
		parsed, err := time.Parse("2006-01-02", source.MessageFilter.Since)
		if err != nil {
			log.Printf("   WARNING: Invalid since date format '%s', ignoring filter", source.MessageFilter.Since)
		} else {
			configSinceDate = &parsed
		}
	}

	totalMessages := 0
	totalStored := 0

	// Process each mailbox (already filtered by IMAP LIST)
	for _, mailbox := range mailboxes {
		fmt.Printf("   Processing mailbox: %s (mode: %s)\n", mailbox, source.Mode)

		// Get sync metadata to determine incremental sync date
		syncCtx, syncCancel := context.WithTimeout(context.Background(), 10*time.Second)
		syncMetadata, err := couchClient.GetSyncMetadata(syncCtx, dbName, mailbox)
		syncCancel()
		if err != nil {
			log.Printf("   ERROR: Failed to get sync metadata for %s: %v", mailbox, err)
			continue
		}

		// Determine the since date for incremental sync
		var sinceDate *time.Time
		if syncMetadata != nil {
			// Use last sync time for incremental sync
			sinceDate = &syncMetadata.LastSyncTime
			fmt.Printf("     Incremental sync since: %s (last synced %d messages)\n",
				sinceDate.Format("2006-01-02 15:04:05"), syncMetadata.MessageCount)
		} else {
			// First sync - use config since date if available
			sinceDate = configSinceDate
			if sinceDate != nil {
				fmt.Printf("     First sync since: %s (from config)\n", sinceDate.Format("2006-01-02"))
			} else {
				fmt.Printf("     First full sync (no date filter)\n")
			}
		}

		// Retrieve messages from the mailbox
		messages, currentUIDs, err := imapClient.GetMessages(mailbox, sinceDate, maxMessages, &source.MessageFilter)
		if err != nil {
			log.Printf("   ERROR: Failed to get messages from %s: %v", mailbox, err)
			continue
		}

		// Perform sync/archive logic
		mailboxSyncCtx, mailboxSyncCancel := context.WithTimeout(context.Background(), 30*time.Second)
		err = couchClient.SyncMailbox(mailboxSyncCtx, dbName, mailbox, currentUIDs, source.IsSyncMode())
		mailboxSyncCancel()
		if err != nil {
			log.Printf("   ERROR: Failed to sync mailbox %s: %v", mailbox, err)
			continue
		}

		if len(messages) == 0 {
			fmt.Printf("     No new messages found in %s\n", mailbox)
			continue
		}

		fmt.Printf("     Found %d messages in %s\n", len(messages), mailbox)
		totalMessages += len(messages)

		// Convert messages to CouchDB documents
		var docs []*couch.MailDocument
		for _, msg := range messages {
			doc := couch.ConvertMessage(msg, mailbox)
			docs = append(docs, doc)
		}

		// Store messages in CouchDB with attachments
		ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
		stored := 0
		for i, doc := range docs {
			err := couchClient.StoreMessage(ctx, dbName, doc, messages[i])
			if err != nil {
				log.Printf("     ERROR: Failed to store message %s: %v", doc.ID, err)
			} else {
				stored++
			}
		}
		cancel()

		fmt.Printf("     Stored %d/%d messages from %s\n", stored, len(messages), mailbox)
		totalStored += stored

		// Update sync metadata after successful processing
		if len(messages) > 0 {
			// Find the highest UID processed
			var maxUID uint32
			for _, msg := range messages {
				if msg.UID > maxUID {
					maxUID = msg.UID
				}
			}

			// Create/update sync metadata
			newMetadata := &couch.SyncMetadata{
				Mailbox:        mailbox,
				LastSyncTime:   time.Now(),
				LastMessageUID: maxUID,
				MessageCount:   stored,
			}

			// Store sync metadata
			metadataCtx, metadataCancel := context.WithTimeout(context.Background(), 10*time.Second)
			err = couchClient.StoreSyncMetadata(metadataCtx, dbName, newMetadata)
			metadataCancel()
			if err != nil {
				log.Printf("     WARNING: Failed to store sync metadata for %s: %v", mailbox, err)
			} else {
				fmt.Printf("     Updated sync metadata (last UID: %d)\n", maxUID)
			}
		}
	}

	fmt.Printf("   Summary: Processed %d messages, stored %d new messages\n", totalMessages, totalStored)
	return nil
}
