import UIKit
import DcCore
import Intents

// this is also used as ChatDetail for SingleChats
class ContactDetailViewController: UITableViewController {
    private let viewModel: ContactDetailViewModel

    private lazy var headerCell: ContactDetailHeader = {
        return ContactDetailHeader()
    }()


    private lazy var startChatCell: ActionCell = {
        let cell = ActionCell()
        cell.actionColor = SystemColor.blue.uiColor
        cell.actionTitle = String.localized("send_message")
        return cell
    }()

    private lazy var ephemeralMessagesCell: UITableViewCell = {
        let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
        cell.textLabel?.text = String.localized("ephemeral_messages")
        cell.accessoryType = .disclosureIndicator
        return cell
    }()

    private lazy var showEncrInfoCell: ActionCell = {
        let cell = ActionCell()
        cell.actionTitle = String.localized("encryption_info_title_desktop")
        cell.actionColor = SystemColor.blue.uiColor
        return cell
    }()

    private lazy var copyToClipboardCell: ActionCell = {
        let cell = ActionCell()
        cell.actionTitle = String.localized("menu_copy_to_clipboard")
        cell.actionColor = SystemColor.blue.uiColor
        return cell
    }()

    private lazy var blockContactCell: ActionCell = {
        let cell = ActionCell()
        cell.actionTitle = viewModel.contact.isBlocked ? String.localized("menu_unblock_contact") : String.localized("menu_block_contact")
        cell.actionColor = viewModel.contact.isBlocked ? SystemColor.blue.uiColor : UIColor.red
        return cell
    }()

    private lazy var muteChatCell: ActionCell = {
        let cell = ActionCell()
        cell.actionTitle = viewModel.chatIsMuted ? String.localized("menu_unmute") :  String.localized("menu_mute")
        cell.actionColor = SystemColor.blue.uiColor
        return cell
    }()

    private lazy var archiveChatCell: ActionCell = {
        let cell = ActionCell()
        cell.actionTitle = viewModel.chatIsArchived ? String.localized("menu_unarchive_chat") :  String.localized("menu_archive_chat")
        cell.actionColor = SystemColor.blue.uiColor
        return cell
    }()

    private lazy var deleteChatCell: ActionCell = {
        let cell = ActionCell()
        cell.actionTitle = String.localized("menu_delete_chat")
        cell.actionColor = UIColor.red
        return cell
    }()

    private lazy var galleryCell: UITableViewCell = {
        let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
        cell.textLabel?.text = String.localized("images_and_videos")
        cell.accessoryType = .disclosureIndicator
        if viewModel.chatId == 0 {
            cell.isUserInteractionEnabled = false
            cell.textLabel?.isEnabled = false
        }
        return cell
    }()

    private lazy var documentsCell: UITableViewCell = {
        let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
        cell.textLabel?.text = String.localized("files")
        cell.accessoryType = .disclosureIndicator
        if viewModel.chatId == 0 {
            cell.isUserInteractionEnabled = false
            cell.textLabel?.isEnabled = false
        }
        return cell
    }()

    private lazy var searchCell: UITableViewCell = {
        let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
        cell.textLabel?.text = String.localized("search")
        cell.accessoryType = .disclosureIndicator
        if viewModel.chatId == 0 {
            cell.isUserInteractionEnabled = false
            cell.textLabel?.isEnabled = false
        }
        return cell
    }()

    private lazy var statusCell: MultilineLabelCell = {
        let cell = MultilineLabelCell()
        cell.multilineDelegate = self
        return cell
    }()

    private var incomingMsgsObserver: NSObjectProtocol?
    private var contactChangedObserver: NSObjectProtocol?

    init(dcContext: DcContext, contactId: Int) {
        self.viewModel = ContactDetailViewModel(dcContext: dcContext, contactId: contactId)
        super.init(style: .grouped)
    }

    required init?(coder _: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // MARK: - lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        configureTableView()
        if !self.viewModel.isSavedMessages && !self.viewModel.isDeviceTalk {
            navigationItem.rightBarButtonItem = UIBarButtonItem(
                title: String.localized("global_menu_edit_desktop"),
                style: .plain, target: self, action: #selector(editButtonPressed))
            self.title = String.localized("tab_contact")
        } else {
            self.title = String.localized("profile")
        }
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        setupObservers()
        updateHeader() // maybe contact name has been edited
        updateCellValues()
        tableView.reloadData()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        removeObservers()
    }

    // MARK: - setup and configuration
    private func configureTableView() {
        tableView.register(ActionCell.self, forCellReuseIdentifier: ActionCell.reuseIdentifier)
        tableView.register(ContactCell.self, forCellReuseIdentifier: ContactCell.reuseIdentifier)
        headerCell.frame = CGRect(0, 0, tableView.frame.width, ContactCell.cellHeight)
        tableView.tableHeaderView = headerCell
        tableView.sectionHeaderHeight =  UITableView.automaticDimension
        tableView.rowHeight = UITableView.automaticDimension
    }

    // MARK: - UITableViewDatasource, UITableViewDelegate
    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        if previousTraitCollection?.preferredContentSizeCategory !=
            traitCollection.preferredContentSizeCategory {
            headerCell.frame = CGRect(0, 0, tableView.frame.width, ContactCell.cellHeight)
        }
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return viewModel.numberOfSections
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.numberOfRowsInSection(section)
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let row = indexPath.row
        let cellType = viewModel.typeFor(section: indexPath.section)
        switch cellType {
        case .chatOptions:
            switch viewModel.chatOptionFor(row: row) {
            case .documents:
                return documentsCell
            case .gallery:
                return galleryCell
            case .search:
                return searchCell
            case .ephemeralMessages:
                return ephemeralMessagesCell
            case .muteChat:
                return muteChatCell
            case .startChat:
                return startChatCell
            }
        case .statusArea:
            return statusCell
        case .chatActions:
            switch viewModel.chatActionFor(row: row) {
            case .archiveChat:
                return archiveChatCell
            case .showEncrInfo:
                return showEncrInfoCell
            case .copyToClipboard:
                return copyToClipboardCell
            case .blockContact:
                return blockContactCell
            case .deleteChat:
                return deleteChatCell
            }
        case .sharedChats:
            if let cell = tableView.dequeueReusableCell(withIdentifier: ContactCell.reuseIdentifier, for: indexPath) as? ContactCell {
                viewModel.update(sharedChatCell: cell, row: row)
                cell.backgroundColor = DcColors.sharedChatCellBackgroundColor
                return cell
            }
        }
        return UITableViewCell() // should never get here
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let type = viewModel.typeFor(section: indexPath.section)
        switch type {
        case .chatOptions:
            handleChatOption(indexPath: indexPath)
        case .statusArea:
            break
        case .chatActions:
            handleChatAction(indexPath: indexPath)
        case .sharedChats:
            let chatId = viewModel.getSharedChatIdAt(indexPath: indexPath)
            showChat(chatId: chatId)
        }
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let type = viewModel.typeFor(section: indexPath.section)
        switch type {
        case .sharedChats:
            return ContactCell.cellHeight
        default:
            return UITableView.automaticDimension
        }
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return viewModel.titleFor(section: section)
    }

    // MARK: - observers
    private func setupObservers() {
        let nc = NotificationCenter.default
        contactChangedObserver = nc.addObserver(
            forName: dcNotificationContactChanged,
            object: nil,
            queue: OperationQueue.main) { [weak self] notification in
            guard let self = self else { return }
            if let ui = notification.userInfo,
               self.viewModel.contactId == ui["contact_id"] as? Int {
                self.updateHeader()
            }
        }
        incomingMsgsObserver = nc.addObserver(
            forName: dcNotificationIncoming,
            object: nil,
            queue: OperationQueue.main) { [weak self] notification in
            guard let self = self else { return }
            if let ui = notification.userInfo,
               let chatId = ui["chat_id"] as? Int {
                if self.viewModel.getSharedChatIds().contains(chatId) {
                    self.viewModel.updateSharedChats()
                    if self.viewModel.chatId == chatId {
                        self.updateCellValues()
                    }
                    self.tableView.reloadData()
                }
            }
        }
    }

    private func removeObservers() {
        let nc = NotificationCenter.default
        if let contactChangedObserver = self.contactChangedObserver {
            nc.removeObserver(contactChangedObserver)
        }
        if let incomingMsgsObserver = self.incomingMsgsObserver {
            nc.removeObserver(incomingMsgsObserver)
        }
    }

    // MARK: - updates
    private func updateHeader() {
        if viewModel.isSavedMessages {
            let chat = viewModel.context.getChat(chatId: viewModel.chatId)
            headerCell.updateDetails(title: chat.name, subtitle: String.localized("chat_self_talk_subtitle"))
            if let img = chat.profileImage {
                headerCell.setImage(img)
            } else {
                headerCell.setBackupImage(name: chat.name, color: chat.color)
            }
            headerCell.setVerified(isVerified: false)
        } else {
            headerCell.updateDetails(title: viewModel.contact.displayName,
                                     subtitle: viewModel.isDeviceTalk ? String.localized("device_talk_subtitle") : viewModel.contact.email)
            if let img = viewModel.contact.profileImage {
                headerCell.setImage(img)
            } else {
                headerCell.setBackupImage(name: viewModel.contact.displayName, color: viewModel.contact.color)
            }
            headerCell.setVerified(isVerified: viewModel.contact.isVerified)
        }
        headerCell.onAvatarTap = showContactAvatarIfNeeded
    }

    private func updateCellValues() {
        ephemeralMessagesCell.detailTextLabel?.text = String.localized(viewModel.chatIsEphemeral ? "on" : "off")
        galleryCell.detailTextLabel?.text = String.numberOrNone(viewModel.galleryItemMessageIds.count)
        documentsCell.detailTextLabel?.text = String.numberOrNone(viewModel.documentItemMessageIds.count)
        statusCell.setText(text: viewModel.contact.status)
    }

    // MARK: - actions
    private func handleChatAction(indexPath: IndexPath) {
        let action = viewModel.chatActionFor(row: indexPath.row)
        switch action {
        case .archiveChat:
            tableView.deselectRow(at: indexPath, animated: true) // animated as no other elements pop up
            toggleArchiveChat()
        case .showEncrInfo:
            tableView.deselectRow(at: indexPath, animated: false)
            showEncrInfoAlert()
        case .copyToClipboard:
            tableView.deselectRow(at: indexPath, animated: true)
            let pasteboard = UIPasteboard.general
            pasteboard.string = viewModel.contact.email
        case .blockContact:
            tableView.deselectRow(at: indexPath, animated: false)
            toggleBlockContact()
        case .deleteChat:
            tableView.deselectRow(at: indexPath, animated: false)
            showDeleteChatConfirmationAlert()
        }
    }

    private func handleChatOption(indexPath: IndexPath) {
        let action = viewModel.chatOptionFor(row: indexPath.row)
        switch action {
        case .documents:
            showDocuments()
        case .gallery:
            showGallery()
        case .search:
            showSearch()
        case .ephemeralMessages:
            showEphemeralMessagesController()
        case .muteChat:
            tableView.deselectRow(at: indexPath, animated: false)
            if viewModel.chatIsMuted {
                self.viewModel.context.setChatMuteDuration(chatId: self.viewModel.chatId, duration: 0)
                muteChatCell.actionTitle = String.localized("menu_mute")
                self.navigationController?.popViewController(animated: true)
            } else {
                showMuteAlert()
            }
        case .startChat:
            tableView.deselectRow(at: indexPath, animated: false)
            let contactId = viewModel.contactId
            chatWith(contactId: contactId)
        }
    }

    private func toggleArchiveChat() {
        let archived = viewModel.toggleArchiveChat()
        if archived {
            self.navigationController?.popToRootViewController(animated: false)
        } else {
            archiveChatCell.actionTitle = String.localized("menu_archive_chat")
        }
    }

    private func updateBlockContactCell() {
        blockContactCell.actionTitle = viewModel.contact.isBlocked ? String.localized("menu_unblock_contact") : String.localized("menu_block_contact")
        blockContactCell.actionColor = viewModel.contact.isBlocked ? SystemColor.blue.uiColor : UIColor.red
    }


    @objc private func editButtonPressed() {
        showEditContact(contactId: viewModel.contactId)
    }

    // MARK: alerts

    private func showDeleteChatConfirmationAlert() {
        let alert = UIAlertController(
            title: nil,
            message: String.localized("ask_delete_chat_desktop"),
            preferredStyle: .safeActionSheet
        )
        alert.addAction(UIAlertAction(title: String.localized("menu_delete_chat"), style: .destructive, handler: { _ in
            self.deleteChat()
        }))
        alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }

    private func showEncrInfoAlert() {
        let alert = UIAlertController(
            title: nil,
            message: self.viewModel.context.getContactEncrInfo(contactId: self.viewModel.contactId),
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: String.localized("ok"), style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }

    private func showEphemeralMessagesController() {
        let ephemeralMessagesController = SettingsEphemeralMessageController(dcContext: viewModel.context, chatId: viewModel.chatId)
        navigationController?.pushViewController(ephemeralMessagesController, animated: true)
    }

    private func showMuteAlert() {
        let alert = UIAlertController(title: String.localized("mute"), message: nil, preferredStyle: .safeActionSheet)
        let forever = -1
        addDurationSelectionAction(to: alert, key: "mute_for_one_hour", duration: Time.oneHour)
        addDurationSelectionAction(to: alert, key: "mute_for_two_hours", duration: Time.twoHours)
        addDurationSelectionAction(to: alert, key: "mute_for_one_day", duration: Time.oneDay)
        addDurationSelectionAction(to: alert, key: "mute_for_seven_days", duration: Time.oneWeek)
        addDurationSelectionAction(to: alert, key: "mute_forever", duration: forever)

        let cancelAction = UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil)
        alert.addAction(cancelAction)
        present(alert, animated: true, completion: nil)
    }

    private func addDurationSelectionAction(to alert: UIAlertController, key: String, duration: Int) {
        let action = UIAlertAction(title: String.localized(key), style: .default, handler: { _ in
            self.viewModel.context.setChatMuteDuration(chatId: self.viewModel.chatId, duration: duration)
            self.muteChatCell.actionTitle = String.localized("menu_unmute")
            self.navigationController?.popViewController(animated: true)
        })
        alert.addAction(action)
    }

    private func toggleBlockContact() {
        if viewModel.contact.isBlocked {
            let alert = UIAlertController(title: String.localized("ask_unblock_contact"), message: nil, preferredStyle: .safeActionSheet)
            alert.addAction(UIAlertAction(title: String.localized("menu_unblock_contact"), style: .default, handler: { _ in
                self.viewModel.unblockContact()
                self.updateBlockContactCell()
            }))
            alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
            present(alert, animated: true, completion: nil)
        } else {
            let alert = UIAlertController(title: String.localized("ask_block_contact"), message: nil, preferredStyle: .safeActionSheet)
            alert.addAction(UIAlertAction(title: String.localized("menu_block_contact"), style: .destructive, handler: { _ in
                self.viewModel.blockContact()
                self.updateBlockContactCell()
            }))
            alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
            present(alert, animated: true, completion: nil)
        }
    }

    private func chatWith(contactId: Int) {
        let chatId = self.viewModel.context.createChatByContactId(contactId: contactId)
        self.showChat(chatId: chatId)
    }

    // MARK: - coordinator
    private func showChat(chatId: Int) {
        if let chatlistViewController = navigationController?.viewControllers[0] {
            let chatViewController = ChatViewController(dcContext: viewModel.context, chatId: chatId)
            navigationController?.setViewControllers([chatlistViewController, chatViewController], animated: true)
        }
    }

    private func showEditContact(contactId: Int) {
        let editContactController = EditContactController(dcContext: viewModel.context, contactIdForUpdate: contactId)
        navigationController?.pushViewController(editContactController, animated: true)
    }

    private func showDocuments() {
        let messageIds: [Int] = viewModel.documentItemMessageIds.reversed()
        let fileGalleryController = DocumentGalleryController(context: viewModel.context, chatId: viewModel.chatId, fileMessageIds: messageIds)
        navigationController?.pushViewController(fileGalleryController, animated: true)
    }

    private func showGallery() {
        let messageIds: [Int] = viewModel.galleryItemMessageIds.reversed()
        let galleryController = GalleryViewController(context: viewModel.context, chatId: viewModel.chatId, mediaMessageIds: messageIds)
        navigationController?.pushViewController(galleryController, animated: true)
    }

    private func showSearch() {
        if let chatViewController = navigationController?.viewControllers.last(where: {
            $0 is ChatViewController
        }) as? ChatViewController {
            chatViewController.activateSearchOnAppear()
            navigationController?.popViewController(animated: true)
        }
    }

    private func showContactAvatarIfNeeded() {
        if viewModel.isSavedMessages {
            let chat = viewModel.context.getChat(chatId: viewModel.chatId)
            if let url = chat.profileImageURL {
                let previewController = PreviewController(dcContext: viewModel.context, type: .single(url))
                previewController.customTitle = chat.name
                present(previewController, animated: true, completion: nil)
            }
        } else if let url = viewModel.contact.profileImageURL {
            let previewController = PreviewController(dcContext: viewModel.context, type: .single(url))
            previewController.customTitle = viewModel.contact.displayName
            present(previewController, animated: true, completion: nil)
        }
    }

    private func deleteChat() {
        if viewModel.chatId == 0 {
            return
        }
        viewModel.context.deleteChat(chatId: viewModel.chatId)
        NotificationManager.removeNotificationsForChat(dcContext: viewModel.context, chatId: viewModel.chatId)
        INInteraction.delete(with: ["\(viewModel.context.id).\(viewModel.chatId)"])

        navigationController?.popViewControllers(viewsToPop: 2, animated: true)
    }
}

extension ContactDetailViewController: MultilineLabelCellDelegate {
    func phoneNumberTapped(number: String) {
        let sanitizedNumber = number.filter("0123456789".contains)
        if let phoneURL = URL(string: "tel://\(sanitizedNumber)") {
            UIApplication.shared.open(phoneURL, options: [:], completionHandler: nil)
        }
    }

    func urlTapped(url: URL) {
        if Utils.isEmail(url: url) {
            let email = Utils.getEmailFrom(url)
            let contactId = viewModel.context.createContact(name: "", email: email)
            let alert = UIAlertController(title: String.localizedStringWithFormat(String.localized("ask_start_chat_with"), email),
                                          message: nil, preferredStyle: .safeActionSheet)
            alert.addAction(UIAlertAction(title: String.localized("start_chat"), style: .default, handler: { [weak self] _ in
                guard let self = self else { return }
                let chatId = self.viewModel.context.createChatByContactId(contactId: contactId)
                if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
                    appDelegate.appCoordinator.showChat(chatId: chatId, clearViewControllerStack: true)
                }
            }))
            alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
            present(alert, animated: true, completion: nil)
        } else {
            UIApplication.shared.open(url)
        }
    }
}
