import EventKit
import Foundation
import MacSyncShared
import Testing
@testable import ICalSync

@MainActor
final class FakeApplier: CalendarEventApplying {
    var calendars: [String: EKCalendar] = [:]
    var events: [String: EKEvent] = [:]

    private(set) var savedCount = 0
    private(set) var removedCount = 0
    private(set) var lastSavedEvent: EKEvent?
    private(set) var lastRemovedEvent: EKEvent?
    var saveError: Error?
    var removeError: Error?

    let store = EKEventStore()

    func calendar(withIdentifier id: String) -> EKCalendar? { calendars[id] }
    func event(withIdentifier id: String) -> EKEvent? { events[id] }
    func makeEvent() -> EKEvent { EKEvent(eventStore: store) }

    func save(_ event: EKEvent, span: EKSpan) throws {
        if let e = saveError { throw e }
        savedCount += 1
        lastSavedEvent = event
    }

    func remove(_ event: EKEvent, span: EKSpan) throws {
        if let e = removeError { throw e }
        removedCount += 1
        lastRemovedEvent = event
    }
}

@Suite("CalendarSender")
@MainActor
struct CalendarSenderTests {
    private func makeItem(action: String, payload: CalendarSendPayload) -> PendingCalendarSend {
        PendingCalendarSend(id: "item-1", action: action, payload: payload, createdAt: "2026-05-13T00:00:00Z")
    }

    @Test("create_event with valid fields returns .sent")
    func createValid() async {
        let fake = FakeApplier()
        // Seed a calendar
        let cal = EKCalendar(for: .event, eventStore: fake.store)
        fake.calendars["cal-1"] = cal

        let sender = CalendarSender(applier: fake)
        let item = makeItem(action: "create_event", payload: CalendarSendPayload(
            calendarIdentifier: "cal-1",
            title: "Test",
            startDate: "2026-05-13T10:00:00Z",
            endDate: "2026-05-13T11:00:00Z"
        ))
        let result = await sender.apply(item)
        guard case .sent = result else {
            Issue.record("expected .sent, got \(result)")
            return
        }
        #expect(fake.savedCount == 1)
        #expect(fake.lastSavedEvent?.title == "Test")
    }

    @Test("create_event missing calendarIdentifier returns .failed")
    func createMissingCalendar() async {
        let fake = FakeApplier()
        let sender = CalendarSender(applier: fake)
        let item = makeItem(action: "create_event", payload: CalendarSendPayload(
            title: "Test",
            startDate: "2026-05-13T10:00:00Z",
            endDate: "2026-05-13T11:00:00Z"
        ))
        let result = await sender.apply(item)
        guard case .failed(let reason) = result else {
            Issue.record("expected .failed")
            return
        }
        #expect(reason.contains("missing calendarIdentifier"))
        #expect(fake.savedCount == 0)
    }

    @Test("create_event with unknown calendar returns .failed")
    func createUnknownCalendar() async {
        let fake = FakeApplier()
        let sender = CalendarSender(applier: fake)
        let item = makeItem(action: "create_event", payload: CalendarSendPayload(
            calendarIdentifier: "missing",
            title: "Test",
            startDate: "2026-05-13T10:00:00Z",
            endDate: "2026-05-13T11:00:00Z"
        ))
        let result = await sender.apply(item)
        guard case .failed(let reason) = result else {
            Issue.record("expected .failed")
            return
        }
        #expect(reason.contains("calendar not found"))
    }

    @Test("create_event with invalid startDate returns .failed")
    func createInvalidDate() async {
        let fake = FakeApplier()
        let cal = EKCalendar(for: .event, eventStore: fake.store)
        fake.calendars["cal-1"] = cal
        let sender = CalendarSender(applier: fake)
        let item = makeItem(action: "create_event", payload: CalendarSendPayload(
            calendarIdentifier: "cal-1",
            title: "Test",
            startDate: "not-a-date",
            endDate: "2026-05-13T11:00:00Z"
        ))
        let result = await sender.apply(item)
        guard case .failed(let reason) = result else {
            Issue.record("expected .failed")
            return
        }
        #expect(reason.contains("invalid startDate"))
    }

    @Test("update_event with no eventIdentifier returns .failed")
    func updateMissingId() async {
        let fake = FakeApplier()
        let sender = CalendarSender(applier: fake)
        let item = makeItem(action: "update_event", payload: CalendarSendPayload(title: "Renamed"))
        let result = await sender.apply(item)
        guard case .failed(let reason) = result else {
            Issue.record("expected .failed")
            return
        }
        #expect(reason.contains("missing eventIdentifier"))
    }

    @Test("update_event with unknown event returns .failed")
    func updateUnknown() async {
        let fake = FakeApplier()
        let sender = CalendarSender(applier: fake)
        let item = makeItem(action: "update_event", payload: CalendarSendPayload(
            eventIdentifier: "ghost",
            title: "Renamed"
        ))
        let result = await sender.apply(item)
        guard case .failed(let reason) = result else {
            Issue.record("expected .failed")
            return
        }
        #expect(reason.contains("event not found"))
    }

    @Test("delete_event with no eventIdentifier returns .failed")
    func deleteMissingId() async {
        let fake = FakeApplier()
        let sender = CalendarSender(applier: fake)
        let item = makeItem(action: "delete_event", payload: CalendarSendPayload())
        let result = await sender.apply(item)
        guard case .failed(let reason) = result else {
            Issue.record("expected .failed")
            return
        }
        #expect(reason.contains("missing eventIdentifier"))
    }

    @Test("delete_event happy path calls remove")
    func deleteHappy() async {
        let fake = FakeApplier()
        let ev = EKEvent(eventStore: fake.store)
        fake.events["ev-1"] = ev
        let sender = CalendarSender(applier: fake)
        let item = makeItem(action: "delete_event", payload: CalendarSendPayload(eventIdentifier: "ev-1"))
        let result = await sender.apply(item)
        guard case .sent = result else {
            Issue.record("expected .sent, got \(result)")
            return
        }
        #expect(fake.removedCount == 1)
    }

    @Test("unknown action returns .failed")
    func unknownAction() async {
        let fake = FakeApplier()
        let sender = CalendarSender(applier: fake)
        let item = makeItem(action: "bogus", payload: CalendarSendPayload())
        let result = await sender.apply(item)
        guard case .failed(let reason) = result else {
            Issue.record("expected .failed")
            return
        }
        #expect(reason.contains("unknown action"))
    }
}
