//
//  EmailService.swift
//  Confab
//
//  IMAP IDLE email monitoring service for AI email handler
//
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
// Copyright (C) 2026 Rune Olsen
//
// This file is part of Confab.
//
// Confab is licensed under the PolyForm Noncommercial License 1.0.0.
// You may use, study, modify, and share it for any noncommercial
// purpose. Commercial use — including selling Confab or any part of
// it, standalone or bundled into another product or service —
// requires a separate commercial license from the copyright holder.
//
// See the LICENSE file or
// <https://polyformproject.org/licenses/noncommercial/1.0.0> for
// the full license text. For commercial licensing, contact Rune
// Olsen via <https://confab.no>.


import Foundation
import os

// MARK: - Email Models

struct IncomingEmail: Identifiable {
    let id: UUID
    let uid: UInt32
    let messageId: String
    let from: String
    let to: [String]
    let subject: String
    let body: String
    let receivedDate: Date
    let inReplyTo: String? // For threading
}

enum EmailServiceError: LocalizedError {
    case notConfigured
    case connectionFailed(String)
    case authenticationFailed
    case invalidCredentials
    case idleNotSupported
    case sendingFailed(String)

    var errorDescription: String? {
        switch self {
        case .notConfigured:
            return "Email not configured. Check Settings > Email."
        case .connectionFailed(let msg):
            return "Connection failed: \(msg)"
        case .authenticationFailed:
            return "Authentication failed. Check credentials."
        case .invalidCredentials:
            return "Invalid email credentials"
        case .idleNotSupported:
            return "IMAP IDLE not supported by server"
        case .sendingFailed(let msg):
            return "Failed to send email: \(msg)"
        }
    }
}

// MARK: - EmailService

@Observable
final class EmailService {
    static let shared = EmailService()

    private let settings = SettingsService.shared
    private let log = Logger(subsystem: Log.subsystem, category: "email")

    // IMAP IDLE state
    private var isConnected = false
    private var isIdling = false
    private var monitoringTask: Task<Void, Never>?

    // Callback for new emails
    var onNewEmail: ((IncomingEmail) -> Void)?

    private init() {}

    // MARK: - Connection Management

    /// Start IMAP IDLE monitoring (called at app startup)
    func startMonitoring() {
        guard settings.emailHandlerEnabled else {
            log.info("Email handler disabled, skipping monitoring")
            return
        }

        guard settings.emailHandlerConfigured else {
            log.warning("Email handler not configured")
            return
        }

        // Email credentials should be configured in Settings > Email tab
        // (This check can be expanded when email settings are added)

        log.info("Starting IMAP IDLE monitoring...")

        monitoringTask = Task { [weak self] in
            await self?.monitorInbox()
        }
    }

    /// Stop IMAP IDLE monitoring
    func stopMonitoring() {
        log.info("Stopping IMAP IDLE monitoring...")
        monitoringTask?.cancel()
        monitoringTask = nil
        isIdling = false
        isConnected = false
    }

    // MARK: - IMAP Polling Implementation

    private func monitorInbox() async {
        guard let host = settings.emailImapHost,
              let username = settings.emailUsername,
              let password = settings.emailPassword else {
            log.error("Email credentials not configured")
            return
        }

        var checkedUIDs = Set<UInt32>()
        var retryDelay: UInt64 = 30  // Start with 30 seconds

        while !Task.isCancelled {
            do {
                let client = IMAPClient(
                    host: host,
                    port: UInt16(settings.emailImapPort),
                    username: username,
                    password: password,
                    useTLS: true
                )

                try await client.connect()
                try await client.login()
                try await client.selectMailbox("INBOX")

                // Search for ALL unseen emails first
                let allUnseenUIDs = try await client.searchUnseen()

                // Remove UIDs that are no longer unseen (emails were deleted/marked read)
                checkedUIDs = checkedUIDs.intersection(Set(allUnseenUIDs))

                // Check each email for the subject identifier
                for uid in allUnseenUIDs {
                    // Skip if we've already checked this UID (for non-matching emails only)
                    if checkedUIDs.contains(uid) {
                        continue
                    }

                    do {
                        let email = try await client.fetchEmail(uid: uid)

                        // Check if email has the correct subject identifier
                        if email.subject.contains(self.settings.emailSubjectIdentifier) {
                            // Valid email - process it (don't add to checkedUIDs, handler will delete it)
                            log.info("Found matching email: \(email.subject) from \(email.from)")

                            // Call callback on main thread
                            await MainActor.run {
                                onNewEmail?(email)
                            }
                        } else {
                            // Wrong subject - delete it and remember we checked it
                            log.warning("Deleting email without subject identifier: \(email.subject) from \(email.from)")
                            try await client.deleteEmail(uid: uid)
                            checkedUIDs.insert(uid)  // Only track non-matching emails
                        }
                    } catch {
                        log.error("Failed to fetch/process email \(uid): \(error.localizedDescription)")
                        checkedUIDs.insert(uid)  // Track failed emails to avoid retry loops
                    }
                }

                // Expunge deleted messages
                try await client.expunge()

                client.disconnect()

                // Reset retry delay on success
                retryDelay = 30

                // Wait before next check
                try await Task.sleep(for: .seconds(retryDelay))

            } catch {
                log.error("IMAP monitoring error: \(error.localizedDescription)")

                // Exponential backoff (max 5 minutes)
                retryDelay = min(retryDelay * 2, 300)

                try? await Task.sleep(for: .seconds(retryDelay))
            }
        }

        log.info("IMAP monitoring stopped")
    }

    /// Connect to IMAP and SMTP servers and verify credentials
    func testConnection() async throws -> String {
        guard let imapHost = settings.emailImapHost,
              let smtpHost = settings.emailSmtpHost,
              let username = settings.emailUsername,
              let password = settings.emailPassword else {
            throw EmailServiceError.notConfigured
        }

        // Test IMAP connection
        let imapClient = IMAPClient(
            host: imapHost,
            port: UInt16(settings.emailImapPort),
            username: username,
            password: password,
            useTLS: true
        )

        try await imapClient.connect()
        try await imapClient.login()
        try await imapClient.selectMailbox("INBOX")
        imapClient.disconnect()

        // Test SMTP connection
        let smtpClient = SMTPClient(
            host: smtpHost,
            port: UInt16(settings.emailSmtpPort),
            username: username,
            password: password
        )

        try await smtpClient.connect()
        smtpClient.disconnect()

        return "Successfully connected to IMAP (\(imapHost)) and SMTP (\(smtpHost))"
    }

    // MARK: - Email Sending (SMTP)

    /// Send email response via SMTP
    func sendEmail(
        to: String,
        subject: String,
        body: String,
        htmlBody: String? = nil,
        inReplyTo: String? = nil
    ) async throws -> String {
        guard let host = settings.emailSmtpHost,
              let username = settings.emailUsername,
              let password = settings.emailPassword else {
            throw EmailServiceError.notConfigured
        }

        let client = SMTPClient(
            host: host,
            port: UInt16(settings.emailSmtpPort),
            username: username,
            password: password
        )

        let messageId = try await client.sendEmail(
            from: username,
            to: [to],
            subject: subject,
            body: body,
            htmlBody: htmlBody,
            inReplyTo: inReplyTo
        )

        log.info("Email sent successfully: \(messageId)")
        return messageId
    }

    // MARK: - Email Fetching (for manual retrieval)

    /// Fetch recent emails from INBOX (for testing/debugging)
    func fetchRecentEmails(limit: Int = 10) async throws -> [IncomingEmail] {
        // TODO: Implement IMAP FETCH
        // 1. Connect to IMAP server
        // 2. SELECT INBOX
        // 3. SEARCH or FETCH recent UIDs
        // 4. FETCH headers and body for each UID
        // 5. Parse into IncomingEmail structs

        log.warning("IMAP FETCH implementation pending")
        return []
    }

    // MARK: - Helper Methods

    /// Check if email subject contains the identifier
    private func matchesSubjectIdentifier(_ subject: String) -> Bool {
        let identifier = settings.emailSubjectIdentifier
        return subject.contains(identifier)
    }

    /// Extract plain text body from email
    private func extractPlainText(from body: String) -> String {
        // TODO: Handle multipart MIME, HTML stripping, etc.
        return body
    }
}

// MARK: - Implementation Notes

/*
 IMAP IDLE Implementation Options:

 1. MailCore2 (Recommended for production)
    - Add via SPM: https://github.com/MailCore/mailcore2
    - Create Objective-C bridging header
    - Use MCOIMAPSession with MCOIMAPIdleOperation
    - Pros: Battle-tested, full IMAP/SMTP support
    - Cons: Objective-C bridging required

 2. Swift NIO + Custom IMAP
    - Implement IMAP protocol manually
    - Pros: Pure Swift, no bridging
    - Cons: Complex, error-prone, time-consuming

 3. Third-party Swift Libraries
    - Research Swift-native IMAP libraries on GitHub/SPM
    - Pros: Easier than custom implementation
    - Cons: May not be as mature as MailCore2

 For now, this service provides the interface and structure.
 The actual IMAP IDLE implementation should be added based on
 chosen library/approach.
 */
