import Alamofire
import Foundation
import LilithAgentCore
import LilithLogging
import MacSyncShared
import SwiftyJSON

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

// MARK: - Payload Types

public struct EmailAddressPayload {
    public let address: String
    public let name: String?

    var dictionary: [String: Any?] { ["address": address, "name": name] }
}

public struct SyncEmailPayload {
    public let messageId: String
    public let threadId: String?
    public let subject: String?
    public let fromAddress: String
    public let fromName: String?
    public let to: [EmailAddressPayload]
    public let cc: [EmailAddressPayload]
    public let folder: String
    public let direction: String
    public let textBody: String?
    public let htmlBody: String?
    public let hasAttachments: Bool?
    public let attachmentCount: Int?
    public let sentAt: String
    public let receivedAt: String?
    public let isRead: Bool?

    var dictionary: [String: Any?] {
        [
            "messageId": messageId,
            "threadId": threadId,
            "subject": subject,
            "fromAddress": fromAddress,
            "fromName": fromName,
            "to": to.map { $0.dictionary },
            "cc": cc.map { $0.dictionary },
            "folder": folder,
            "direction": direction,
            "textBody": textBody,
            "htmlBody": htmlBody,
            "hasAttachments": hasAttachments,
            "attachmentCount": attachmentCount,
            "sentAt": sentAt,
            "receivedAt": receivedAt,
            "isRead": isRead,
        ]
    }
}

public struct IMailStatsResponse {
    public let totalEmails: Int
    public let totalFolders: Int
    public let lastSyncAt: Date?
}

// MARK: - Protocol

public protocol IMailAPIClientProtocol: AnyObject, Sendable {
    var isAuthenticated: Bool { get }
    func syncMail(_ payloads: [SyncEmailPayload]) async throws -> Int
    func getStats() async throws -> IMailStatsResponse
    func getPendingSends() async throws -> [PendingMailSend]
    func reportSendResult(id: String, status: String, error: String?) async throws
}

// MARK: - APIClient

public final class APIClient: BaseAPIClient, IMailAPIClientProtocol, @unchecked Sendable {
    public static let shared: APIClient = {
        APIClient(baseURL: macSyncResolveServerURL(), keychain: macSyncSharedKeychain)
    }()

    public var isAuthenticated: Bool {
        (try? getAuthToken()) != nil
    }

    // MARK: - Mail Sync

    public func syncMail(_ payloads: [SyncEmailPayload]) async throws -> Int {
        let params: [String: Any] = ["emails": payloads.map { $0.dictionary }]
        let data = try await authenticatedRequest("/client/imail/sync", method: .post, parameters: params)
        let json = JSON(data)
        guard json["success"].boolValue else {
            let msg = json["error"]["message"].stringValue
            throw APIError.serverError(statusCode: json["statusCode"].intValue,
                                       message: msg.isEmpty ? "Server error" : msg)
        }
        let synced = json["data"]["synced"].intValue
        log.info("syncMail success synced=\(synced)")
        return synced
    }

    // MARK: - Send Queue

    public func getPendingSends() async throws -> [PendingMailSend] {
        let data = try await authenticatedRequest("/client/imail/send-queue/pending", method: .get)
        let json = JSON(data)
        guard json["success"].boolValue else {
            throw APIError.serverError(statusCode: 0, message: json["error"]["message"].stringValue)
        }
        return json["data"]["items"].arrayValue.map { item in
            let p = item["payload"]
            let cc = p["cc"].array?.compactMap { $0.string }
            let bcc = p["bcc"].array?.compactMap { $0.string }
            return PendingMailSend(
                id: item["id"].stringValue,
                action: item["action"].stringValue,
                payload: MailSendPayload(
                    to: p["to"].stringValue,
                    cc: cc,
                    bcc: bcc,
                    subject: p["subject"].stringValue,
                    body: p["body"].stringValue,
                    isHtml: p["isHtml"].bool
                ),
                createdAt: item["createdAt"].stringValue
            )
        }
    }

    public func reportSendResult(id: String, status: String, error: String?) async throws {
        var params: [String: Any] = ["status": status]
        if let err = error { params["error"] = err }
        let data = try await authenticatedRequest("/client/imail/send-queue/\(id)/result", method: .post, parameters: params)
        let json = JSON(data)
        guard json["success"].boolValue else {
            throw APIError.serverError(statusCode: 0, message: json["error"]["message"].stringValue)
        }
    }

    // MARK: - Stats

    public func getStats() async throws -> IMailStatsResponse {
        let data = try await authenticatedRequest("/client/imail/stats", method: .get)
        let json = JSON(data)
        guard json["success"].boolValue else {
            throw APIError.serverError(statusCode: 0, message: json["error"]["message"].stringValue)
        }
        return IMailStatsResponse(
            totalEmails: json["data"]["totalEmails"].intValue,
            totalFolders: json["data"]["totalFolders"].intValue,
            lastSyncAt: json["data"]["lastSyncAt"].string.flatMap { ISO8601DateFormatter().date(from: $0) }
        )
    }
}
