import Foundation
import MacSyncShared
import Testing
@testable import IMailSync

@MainActor
final class FakeMailApplier: MailSendApplying {
    private(set) var sent: [MailSendPayload] = []
    var result: SendQueueApplyResult = .sent

    func send(_ payload: MailSendPayload) async -> SendQueueApplyResult {
        sent.append(payload)
        return result
    }
}

@Suite("MailSender dispatch")
@MainActor
struct MailSenderTests {
    private func makeItem(action: String, payload: MailSendPayload) -> PendingMailSend {
        PendingMailSend(id: "item-1", action: action, payload: payload, createdAt: "2026-05-13T00:00:00Z")
    }

    @Test("FakeApplier records the payload and returns .sent")
    func dispatchSent() async {
        let fake = FakeMailApplier()
        fake.result = .sent
        let payload = MailSendPayload(
            to: "alice@example.com",
            subject: "Hello",
            body: "Hi"
        )
        let result = await fake.send(payload)
        guard case .sent = result else {
            Issue.record("expected .sent, got \(result)")
            return
        }
        #expect(fake.sent.count == 1)
        #expect(fake.sent.first?.to == "alice@example.com")
        #expect(fake.sent.first?.subject == "Hello")
    }

    @Test("Failed sends surface a reason")
    func dispatchFailed() async {
        let fake = FakeMailApplier()
        fake.result = .failed(reason: "Mail.app unavailable")
        let result = await fake.send(MailSendPayload(to: "x@example.com", subject: "s", body: "b"))
        guard case .failed(let reason) = result else {
            Issue.record("expected .failed")
            return
        }
        #expect(reason == "Mail.app unavailable")
    }

    @Test("PendingMailSend decodes from JSON")
    func decodePending() throws {
        let json = """
        {
          "id": "abc-123",
          "action": "send_mail",
          "createdAt": "2026-05-13T00:00:00Z",
          "payload": {
            "to": "alice@example.com",
            "cc": ["c@example.com"],
            "subject": "Hi",
            "body": "Body",
            "isHtml": false
          }
        }
        """.data(using: .utf8)!
        let item = try JSONDecoder().decode(PendingMailSend.self, from: json)
        #expect(item.id == "abc-123")
        #expect(item.action == "send_mail")
        #expect(item.payload.to == "alice@example.com")
        #expect(item.payload.cc == ["c@example.com"])
        #expect(item.payload.isHtml == false)
    }

    @Test("MailSendPayload tolerates omitted optionals")
    func decodeMinimal() throws {
        let json = """
        { "to": "a@example.com", "subject": "s", "body": "b" }
        """.data(using: .utf8)!
        let payload = try JSONDecoder().decode(MailSendPayload.self, from: json)
        #expect(payload.to == "a@example.com")
        #expect(payload.cc == nil)
        #expect(payload.bcc == nil)
        #expect(payload.isHtml == nil)
    }
}
