//
//  Notifications.swift
//  SimpleX
//
//  Created by Evgeny on 28/04/2022.
//  Copyright © 2022 SimpleX Chat. All rights reserved.
//

import Foundation
import UserNotifications
import SwiftUI

public let ntfCategoryContactRequest = "NTF_CAT_CONTACT_REQUEST"
public let ntfCategoryContactConnected = "NTF_CAT_CONTACT_CONNECTED"
public let ntfCategoryMessageReceived = "NTF_CAT_MESSAGE_RECEIVED"
public let ntfCategoryCallInvitation = "NTF_CAT_CALL_INVITATION"
public let ntfCategoryConnectionEvent = "NTF_CAT_CONNECTION_EVENT"
public let ntfCategoryCheckMessage = "NTF_CAT_CHECK_MESSAGE"

public let appNotificationId = "chat.simplex.app.notification"

let contactHidden = NSLocalizedString("Contact hidden:", comment: "notification")

public func createContactRequestNtf(_ user: any UserLike, _ contactRequest: UserContactRequest) -> UNMutableNotificationContent {
    let hideContent = ntfPreviewModeGroupDefault.get() == .hidden
    return createNotification(
        categoryIdentifier: ntfCategoryContactRequest,
        title: String.localizedStringWithFormat(
            NSLocalizedString("%@ wants to connect!", comment: "notification title"),
            hideContent ? NSLocalizedString("Somebody", comment: "notification title") : contactRequest.displayName
        ),
        body: String.localizedStringWithFormat(
            NSLocalizedString("Accept contact request from %@?", comment: "notification body"),
            hideContent ? NSLocalizedString("this contact", comment: "notification title") : contactRequest.chatViewName
        ),
        targetContentIdentifier: nil,
        userInfo: ["chatId": contactRequest.id, "contactRequestId": contactRequest.apiId, "userId": user.userId]
    )
}

public func createContactConnectedNtf(_ user: any UserLike, _ contact: Contact) -> UNMutableNotificationContent {
    let hideContent = ntfPreviewModeGroupDefault.get() == .hidden
    return createNotification(
        categoryIdentifier: ntfCategoryContactConnected,
        title: String.localizedStringWithFormat(
            NSLocalizedString("%@ is connected!", comment: "notification title"),
            hideContent ? NSLocalizedString("A new contact", comment: "notification title") : contact.displayName
        ),
        body: String.localizedStringWithFormat(
            NSLocalizedString("You can now chat with %@", comment: "notification body"),
            hideContent ? NSLocalizedString("this contact", comment: "notification title") : contact.chatViewName
        ),
        targetContentIdentifier: contact.id,
        userInfo: ["userId": user.userId]
//            userInfo: ["chatId": contact.id, "contactId": contact.apiId]
    )
}

public func createMessageReceivedNtf(_ user: any UserLike, _ cInfo: ChatInfo, _ cItem: ChatItem) -> UNMutableNotificationContent {
    let previewMode = ntfPreviewModeGroupDefault.get()
    var title: String
    if case let .group(groupInfo) = cInfo, case let .groupRcv(groupMember) = cItem.chatDir {
        title = groupMsgNtfTitle(groupInfo, groupMember, hideContent: previewMode == .hidden)
    } else {
        title = previewMode == .hidden ? contactHidden : "\(cInfo.chatViewName):"
    }
    return createNotification(
        categoryIdentifier: ntfCategoryMessageReceived,
        title: title,
        body: previewMode == .message ? hideSecrets(cItem) : NSLocalizedString("new message", comment: "notification"),
        targetContentIdentifier: cInfo.id,
        userInfo: ["userId": user.userId]
//            userInfo: ["chatId": cInfo.id, "chatItemId": cItem.id]
    )
}

public func createCallInvitationNtf(_ invitation: RcvCallInvitation) -> UNMutableNotificationContent {
    let text = invitation.callType.media == .video
                ? NSLocalizedString("Incoming video call", comment: "notification")
                : NSLocalizedString("Incoming audio call", comment: "notification")
    let hideContent = ntfPreviewModeGroupDefault.get() == .hidden
    return createNotification(
        categoryIdentifier: ntfCategoryCallInvitation,
        title: hideContent ? contactHidden : "\(invitation.contact.chatViewName):",
        body: text,
        targetContentIdentifier: nil,
        userInfo: ["chatId": invitation.contact.id, "userId": invitation.user.userId]
    )
}

public func createConnectionEventNtf(_ user: User, _ connEntity: ConnectionEntity) -> UNMutableNotificationContent {
    let hideContent = ntfPreviewModeGroupDefault.get() == .hidden
    var title: String
    var body: String? = nil
    var targetContentIdentifier: String? = nil
    switch connEntity {
    case let .rcvDirectMsgConnection(contact):
        if let contact = contact {
            title = hideContent ? contactHidden : "\(contact.chatViewName):"
            targetContentIdentifier = contact.id
        } else {
            title = NSLocalizedString("New contact:", comment: "notification")
        }
        body = NSLocalizedString("message received", comment: "notification")
    case let .rcvGroupMsgConnection(groupInfo, groupMember):
        title = groupMsgNtfTitle(groupInfo, groupMember, hideContent: hideContent)
        body = NSLocalizedString("message received", comment: "notification")
        targetContentIdentifier = groupInfo.id
    case .sndFileConnection:
        title = NSLocalizedString("Sent file event", comment: "notification")
    case .rcvFileConnection:
        title = NSLocalizedString("Received file event", comment: "notification")
    case .userContactConnection:
        title = NSLocalizedString("New contact request", comment: "notification")
    }
    return createNotification(
        categoryIdentifier: ntfCategoryConnectionEvent,
        title: title,
        body: body,
        targetContentIdentifier: targetContentIdentifier,
        userInfo: ["userId": user.userId]
    )
}

public func createErrorNtf(_ dbStatus: DBMigrationResult) -> UNMutableNotificationContent {
    var title: String
    switch dbStatus {
    case .errorNotADatabase:
        title = NSLocalizedString("Encrypted message: no passphrase", comment: "notification")
    case .errorMigration:
        title = NSLocalizedString("Encrypted message: database migration error", comment: "notification")
    case .errorSQL:
        title = NSLocalizedString("Encrypted message: database error", comment: "notification")
    case .errorKeychain:
        title = NSLocalizedString("Encrypted message: keychain error", comment: "notification")
    case .unknown:
        title = NSLocalizedString("Encrypted message: unexpected error", comment: "notification")
    case .invalidConfirmation:
        title = NSLocalizedString("Encrypted message or another event", comment: "notification")
    case .ok:
        title = NSLocalizedString("Encrypted message or another event", comment: "notification")
    }
    return createNotification(
        categoryIdentifier: ntfCategoryConnectionEvent,
        title: title
    )
}

public func createAppStoppedNtf() -> UNMutableNotificationContent {
    return createNotification(
        categoryIdentifier: ntfCategoryConnectionEvent,
        title: NSLocalizedString("Encrypted message: app is stopped", comment: "notification")
    )
}

private func groupMsgNtfTitle(_ groupInfo: GroupInfo, _ groupMember: GroupMember, hideContent: Bool) -> String {
    hideContent
    ? NSLocalizedString("Group message:", comment: "notification")
    : "#\(groupInfo.displayName) \(groupMember.chatViewName):"
}

public func createNotification(categoryIdentifier: String, title: String, subtitle: String? = nil, body: String? = nil,
                        targetContentIdentifier: String? = nil, userInfo: [AnyHashable : Any] = [:]) -> UNMutableNotificationContent {
    let content = UNMutableNotificationContent()
    content.categoryIdentifier = categoryIdentifier
    content.title = title
    if let s = subtitle { content.subtitle = s }
    if let s = body { content.body = s }
    content.targetContentIdentifier = targetContentIdentifier
    content.userInfo = userInfo
    // TODO move logic of adding sound here, so it applies to background notifications too
    content.sound = .default
//        content.interruptionLevel = .active
//        content.relevanceScore = 0.5 // 0-1
    return content
}

func hideSecrets(_ cItem: ChatItem) -> String {
    if let md = cItem.formattedText {
        var res = ""
        for ft in md {
            if case .secret = ft.format {
                res = res + "..."
            } else {
                res = res + ft.text
            }
        }
        return res
    } else {
        return cItem.text
    }
}
