import Foundation
import LilithLogging
import MacSyncShared

private let log = AppLogger.logger(for: "IMail.Sender")

// MARK: - Wire types

public struct MailSendPayload: Sendable, Decodable, Equatable {
    public let to: String
    public let cc: [String]?
    public let bcc: [String]?
    public let subject: String
    public let body: String
    public let isHtml: Bool?

    public init(
        to: String,
        cc: [String]? = nil,
        bcc: [String]? = nil,
        subject: String,
        body: String,
        isHtml: Bool? = nil
    ) {
        self.to = to
        self.cc = cc
        self.bcc = bcc
        self.subject = subject
        self.body = body
        self.isHtml = isHtml
    }
}

public struct PendingMailSend: Sendable, Decodable, Equatable {
    public let id: String
    public let action: String
    public let payload: MailSendPayload
    public let createdAt: String

    public init(id: String, action: String, payload: MailSendPayload, createdAt: String) {
        self.id = id
        self.action = action
        self.payload = payload
        self.createdAt = createdAt
    }
}

// MARK: - Applier protocol (hermetic-test seam)

/// Narrow protocol the `MailSender` calls into so tests can substitute a
/// fake that does not require Mail.app / AppleScript automation access.
@MainActor
public protocol MailSendApplying: Sendable {
    func send(_ payload: MailSendPayload) async -> SendQueueApplyResult
}

// MARK: - Sender

/// Default `MailSendApplying` backed by the existing AppleScript-based
/// `Sender` (Mail.app driver). Adapts the structured `MailSendPayload` to
/// `Sender.SendRequest` and dispatches the synchronous AppleScript call off
/// the main thread.
@MainActor
public final class MailSender: MailSendApplying {
    private let sender: Sender

    public init(sender: Sender = .shared) {
        self.sender = sender
    }

    public func send(_ payload: MailSendPayload) async -> SendQueueApplyResult {
        let request = Sender.SendRequest(
            to: [payload.to],
            cc: payload.cc ?? [],
            bcc: payload.bcc ?? [],
            subject: payload.subject,
            body: payload.body,
            isHTML: payload.isHtml ?? false
        )
        // `Sender.send` runs a blocking AppleScript call — dispatch off main.
        let result = await Task.detached(priority: .userInitiated) { [sender] in
            sender.send(request)
        }.value

        if result.success {
            return .sent
        } else {
            let reason = result.error ?? "unknown send failure"
            log.warning("MailSender.send failed: \(reason)")
            return .failed(reason: reason)
        }
    }
}

// MARK: - SendQueueTransport adapter

public struct IMailSendTransport: SendQueueTransport {
    public typealias PendingItem = PendingMailSend

    private let apiClient: any IMailAPIClientProtocol

    public init(apiClient: any IMailAPIClientProtocol) {
        self.apiClient = apiClient
    }

    public func id(of item: PendingMailSend) -> String { item.id }

    public func fetchPending() async throws -> [PendingMailSend] {
        guard apiClient.isAuthenticated else { return [] }
        return try await apiClient.getPendingSends()
    }

    public func reportResult(id: String, status: String, error: String?) async throws {
        try await apiClient.reportSendResult(id: id, status: status, error: error)
    }
}
