//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import Foundation
import MultipeerConnectivity
import SignalServiceKit
import SignalUI

public class RegistrationTransferQRCodeViewController: OWSViewController, OWSNavigationChildController {

    public var preferredNavigationBarStyle: OWSNavigationBarStyle { .solid }
    public var navbarBackgroundColorOverride: UIColor? { .clear }

    public var prefersNavigationBarHidden: Bool { true }

    public override var preferredStatusBarStyle: UIStatusBarStyle {
        return isQRCodeExpanded ? .lightContent : super.preferredStatusBarStyle
    }

    private lazy var qrCodeView = QRCodeView(useCircularWrapper: false)

    private lazy var expansionButton: ExpansionButton = {
        let button = ExpansionButton()
        button.addTarget(self, action: #selector(toggleQRCodeExpansion), for: .touchUpInside)
        return button
    }()

    private lazy var qrCodeSizeButtonSpacer = SpacerView()

    private lazy var compactQRCodeContainer: UIView = {
        let view = UIView()

        view.layer.cornerRadius = 12
        view.backgroundColor = Theme.secondaryBackgroundColor

        view.addSubview(qrCodeView)
        view.addSubview(expansionButton)

        qrCodeView.autoPinEdge(toSuperviewEdge: .top, withInset: 16)
        expansionButton.autoPinEdge(.top, to: .bottom, of: qrCodeView, withOffset: 12)
        expansionButton.autoPinEdge(toSuperviewEdge: .bottom, withInset: 16)

        qrCodeView.autoPinEdge(toSuperviewEdge: .leading, withInset: 16)
        qrCodeView.autoPinEdge(toSuperviewEdge: .trailing, withInset: 16)

        expansionButton.autoHCenterInSuperview()

        return view
    }()

    private lazy var explanationLabel2: UILabel = {
        let explanationLabel2 = UILabel.explanationLabelForRegistration(
            text: OWSLocalizedString(
                "DEVICE_TRANSFER_QRCODE_EXPLANATION2",
                comment: "The second explanation for the device transfer qr code view"
            )
        )
        explanationLabel2.setContentHuggingHigh()
        return explanationLabel2
    }()

    private lazy var closeButton: UIButton = {
        let closeButton = OWSButton()
        closeButton.setImage(
            Theme.iconImage(.buttonX).asTintedImage(color: .ows_white),
            for: .normal
        )
        closeButton.contentMode = .center
        closeButton.addTarget(self, action: #selector(compactQRCode), for: .touchUpInside)
        return closeButton
    }()

    private let titleLabel = UILabel.titleLabelForRegistration(
        text: OWSLocalizedString(
            "DEVICE_TRANSFER_QRCODE_TITLE",
            comment: "The title for the device transfer qr code view"
        )
    )

    private let explanationLabel = UILabel.explanationLabelForRegistration(
        text: OWSLocalizedString(
            "DEVICE_TRANSFER_QRCODE_EXPLANATION",
            comment: "The explanation for the device transfer qr code view"
        )
    )

    private lazy var helpButton = OWSFlatButton.linkButtonForRegistration(
        title: OWSLocalizedString(
            "DEVICE_TRANSFER_QRCODE_NOT_SEEING",
            comment: "A prompt to provide further explanation if the user is not seeing the transfer on both devices."
        ),
        target: self,
        selector: #selector(didTapHelp)
    )

    private lazy var cancelButton = OWSFlatButton.linkButtonForRegistration(
        title: CommonStrings.cancelButton,
        target: self,
        selector: #selector(didTapCancel)
    )

    override public func loadView() {
        view = UIView()

        view.backgroundColor = Theme.backgroundColor

        view.addSubview(titleLabel)
        titleLabel.accessibilityIdentifier = "onboarding.transferQRCode.titleLabel"
        titleLabel.setContentHuggingHigh()

        explanationLabel.textColor = Theme.primaryTextColor
        explanationLabel.accessibilityIdentifier = "onboarding.transferQRCode.bodyLabel"
        explanationLabel.setContentHuggingHigh()

        qrCodeView.setContentHuggingVerticalLow()

        helpButton.button.titleLabel?.textAlignment = .center
        helpButton.button.titleLabel?.numberOfLines = 0
        helpButton.button.titleLabel?.lineBreakMode = .byWordWrapping

        let stackView = UIStackView(arrangedSubviews: [
            titleLabel,
            explanationLabel,
            compactQRCodeContainer,
            explanationLabel2,
            UIView.vStretchingSpacer(),
            helpButton,
            cancelButton
        ])
        stackView.axis = .vertical
        stackView.alignment = .center
        stackView.spacing = 20
        view.addSubview(stackView)
        stackView.autoPinEdge(toSuperviewMargin: .top, withInset: 20)
        stackView.autoPinLeadingToSuperviewMargin()
        stackView.autoPinTrailingToSuperviewMargin()
        stackView.autoPinBottomToSuperviewMargin()

        qrCodeView.autoPinToSquareAspectRatio()
        qrCodeView.autoMatch(
            .width,
            to: .width,
            of: view,
            withMultiplier: Constants.compactQRCodeWidthMultiple
        ).priority = .defaultHigh
        qrCodeView.autoSetDimension(
            .width,
            toSize: Constants.compactQRCodeMinSize,
            relation: .greaterThanOrEqual
        ).priority = .required

        for view in [titleLabel, explanationLabel, explanationLabel2] {
            view.setCompressionResistanceHigh()
            view.autoPinEdge(toSuperviewMargin: .leading)
            view.autoPinEdge(toSuperviewMargin: .trailing)
        }

        view.addSubview(closeButton)
        closeButton.autoPinLeadingToSuperviewMargin()
        closeButton.autoPinTopToSuperviewMargin(withInset: 8)

        updateExpansionState(animated: false)

        view.addGestureRecognizer(UITapGestureRecognizer(
            target: self,
            action: #selector(compactQRCode)
        ))
    }

    override public func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.setHidesBackButton(true, animated: false)
    }

    override public func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        deviceTransferService.addObserver(self)

        do {
            let url = try deviceTransferService.startAcceptingTransfersFromOldDevices(
                mode: .primary
            )

            qrCodeView.setQR(url: url)
        } catch {
            owsFailDebug("error \(error)")
        }
    }

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

        deviceTransferService.removeObserver(self)
        deviceTransferService.stopAcceptingTransfersFromOldDevices()
    }

    // MARK: - QR Code expansion

    private var isQRCodeExpanded = false

    private func updateExpansionState(animated: Bool) {
        view.isUserInteractionEnabled = false

        let otherViews = [
            titleLabel,
            explanationLabel,
            helpButton,
            cancelButton
        ]

        let animations: () -> Void = {
            if self.isQRCodeExpanded {
                let desiredSize = UIScreen.main.bounds.width - (Constants.expandedQRCodeMargin * 2)
                let qrExpandScale = desiredSize / self.qrCodeView.frame.width
                let currentQRCenterY = self.view.convert(
                    self.qrCodeView.center,
                    from: self.qrCodeView.superview
                ).y
                let desiredQRCenterY = self.view.frame.height * 0.4
                let qrExpandYOffset = desiredQRCenterY - currentQRCenterY
                self.qrCodeView.layer.anchorPoint = .init(x: 0.5, y: 0.5)
                self.qrCodeView.transform = .scale(qrExpandScale)
                    .concatenating(.translate(CGPoint(x: 0, y: qrExpandYOffset)))

                let expandedQRCodeBottom = desiredQRCenterY + (desiredSize / 2)

                let desiredButtonTop = expandedQRCodeBottom + 16
                let currentButtonTop = self.view.convert(
                    self.expansionButton.frame.origin,
                    from: self.expansionButton.superview
                ).y
                let buttonYOffset = max(0, desiredButtonTop - currentButtonTop)
                self.expansionButton.transform = .translate(CGPoint(x: 0, y: buttonYOffset))

                let desiredLabelTop = desiredButtonTop + self.expansionButton.frame.height + 16
                let currentLabelTop = self.view.convert(
                    self.explanationLabel2.frame.origin,
                    from: self.explanationLabel2.superview
                ).y
                let labelYOffset = max(0, desiredLabelTop - currentLabelTop)
                self.explanationLabel2.transform = .translate(CGPoint(x: 0, y: labelYOffset))

                self.explanationLabel2.textColor = Theme.darkThemePrimaryColor
                self.closeButton.alpha = 1
                otherViews.forEach { $0.alpha = 0 }
                self.compactQRCodeContainer.backgroundColor = .clear
                self.view.backgroundColor = .ows_black
                self.qrCodeView.backgroundColor = .ows_white
                self.expansionButton.mode = .contract
            } else {
                self.qrCodeView.transform = .identity
                self.expansionButton.transform = .identity
                self.explanationLabel2.transform = .identity
                self.explanationLabel2.textColor = Theme.primaryTextColor
                self.closeButton.alpha = 0
                otherViews.forEach { $0.alpha = 1 }
                self.compactQRCodeContainer.backgroundColor = Theme.secondaryBackgroundColor
                self.view.backgroundColor = Theme.backgroundColor
                self.qrCodeView.backgroundColor = .ows_white
                self.expansionButton.mode = .expand
            }
        }

        guard animated else {
            animations()
            view.isUserInteractionEnabled = true
            return
        }
        UIView.animate(withDuration: 0.2, animations: animations) { _ in
            self.view.isUserInteractionEnabled = true
        }
    }

    @objc
    private func toggleQRCodeExpansion() {
        isQRCodeExpanded = !isQRCodeExpanded
        updateExpansionState(animated: true)
        setNeedsStatusBarAppearanceUpdate()
    }

    @objc
    private func compactQRCode() {
        guard isQRCodeExpanded else { return }
        toggleQRCodeExpansion()
    }

    // MARK: - Events

    weak var permissionActionSheetController: ActionSheetController?

    @objc
    func didTapHelp() {
        guard !isQRCodeExpanded else { return }
        let turnOnView = TurnOnPermissionView(
            title: OWSLocalizedString(
                "LOCAL_NETWORK_PERMISSION_ACTION_SHEET_TITLE",
                comment: "Title for local network permission action sheet"
            ),
            message: OWSLocalizedString(
                "LOCAL_NETWORK_PERMISSION_ACTION_SHEET_BODY",
                comment: "Body for local network permission action sheet"
            ),
            steps: [
                .init(
                    icon: #imageLiteral(resourceName: "settings-app-icon-32"),
                    text: OWSLocalizedString(
                        "LOCAL_NETWORK_PERMISSION_ACTION_SHEET_STEP_ONE",
                        comment: "First step for local network permission action sheet"
                    )
                ),
                .init(
                    icon: #imageLiteral(resourceName: "AppIcon"),
                    text: OWSLocalizedString(
                        "LOCAL_NETWORK_PERMISSION_ACTION_SHEET_STEP_TWO",
                        comment: "Second step for local network permission action sheet"
                    )
                ),
                .init(
                    icon: UIImage(imageLiteralResourceName: "toggle-32"),
                    text: OWSLocalizedString(
                        "LOCAL_NETWORK_PERMISSION_ACTION_SHEET_STEP_THREE",
                        comment: "Third step for local network permission action sheet"
                    )
                )
            ],
            button: OWSFlatButton.primaryButtonForRegistration(
                title: OWSLocalizedString(
                    "LOCAL_NETWORK_PERMISSION_ACTION_SHEET_NEED_HELP",
                    comment: "A button asking the user if they need further help getting their transfer working."
                ),
                target: self,
                selector: #selector(didTapContactSupport)
            )
        )

        let actionSheetController = ActionSheetController()
        permissionActionSheetController = actionSheetController
        actionSheetController.customHeader = turnOnView
        actionSheetController.isCancelable = true
        presentActionSheet(actionSheetController)
    }

    @objc
    func didTapCancel() {
        guard !isQRCodeExpanded else { return }
        Logger.info("")

        guard let navigationController = navigationController else {
            return owsFailBeta("unexpectedly missing nav controller")
        }

        navigationController.popViewController(animated: true)
    }

    @objc
    func didTapContactSupport() {
        guard !isQRCodeExpanded else { return }
        Logger.info("")

        permissionActionSheetController?.dismiss(animated: true)
        permissionActionSheetController = nil

        ContactSupportAlert.presentStep2(
            emailSupportFilter: "Signal iOS Transfer",
            fromViewController: self
        )
    }

    // MARK: - Expand/Contract Button

    private class ExpansionButton: UIButton {
        enum Mode {
            case expand
            case contract

            var text: String {
                switch self {
                case .expand:
                    return OWSLocalizedString(
                        "DEVICE_TRANSFER_EXPAND_QR_CODE_BUTTON",
                        comment: "Button shown to expand a QR code and view it fullscreen."
                    )
                case .contract:
                    return OWSLocalizedString(
                        "DEVICE_TRANSFER_CONTRACT_QR_CODE_BUTTON",
                        comment: "Button shown to contract a QR code and exit the fullscreen view."
                    )
                }
            }

            var image: UIImage {
                switch self {
                case .expand:
                    return Theme.iconImage(.maximize)
                case .contract:
                    return Theme.iconImage(.minimize)
                }
            }

            var backgroundColor: UIColor {
                switch self {
                case .expand: return .clear
                case .contract: return Theme.darkThemeSecondaryBackgroundColor
                }
            }

            var textColor: UIColor {
                switch self {
                case .expand: return Theme.primaryTextColor
                case .contract: return Theme.darkThemePrimaryColor
                }
            }
        }

        var mode: Mode = .expand {
            didSet {
                updateForMode()
            }
        }

        private lazy var _imageView = UIImageView()
        private lazy var _label = UILabel()

        init() {
            super.init(frame: .zero)

            addSubview(_imageView)
            addSubview(_label)

            _imageView.autoPinLeadingToSuperviewMargin()
            _imageView.autoPinEdge(toSuperviewEdge: .leading, withInset: 12)
            _label.autoPinEdge(.leading, to: .trailing, of: _imageView, withOffset: 4)
            _imageView.autoPinHeight(toHeightOf: _label, offset: -4)
            _imageView.autoPinEdge(
                .top,
                to: .top,
                of: _label,
                withOffset: 2
            )
            _label.autoPinEdge(toSuperviewEdge: .top, withInset: 6, relation: .greaterThanOrEqual)
            _label.autoPinEdge(toSuperviewEdge: .bottom, withInset: 8, relation: .lessThanOrEqual)
            _label.autoPinEdge(toSuperviewEdge: .trailing, withInset: 16)

            _imageView.autoPinToSquareAspectRatio()
            _label.setContentHuggingHigh()
            _imageView.setContentHuggingHigh()

            updateForMode()
        }

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

        private func updateForMode() {
            _label.text = mode.text
            _imageView.image = mode.image
            _imageView.tintColor = mode.textColor
            _label.textColor = mode.textColor
            backgroundColor = mode.backgroundColor
            layer.cornerRadius = bounds.height / 2
        }

        override var bounds: CGRect {
            didSet {
                layer.cornerRadius = bounds.height / 2
            }
        }
    }

    // MARK: - Constants

    private enum Constants {
        static let compactQRCodeWidthMultiple: CGFloat = 0.6
        static let compactQRCodeMinSize: CGFloat = 182

        static let expandedQRCodeMargin: CGFloat = 8
    }
}

extension RegistrationTransferQRCodeViewController: DeviceTransferServiceObserver {
    func deviceTransferServiceDiscoveredNewDevice(peerId: MCPeerID, discoveryInfo: [String: String]?) {}

    func deviceTransferServiceDidStartTransfer(progress: Progress) {
        let view = RegistrationTransferProgressViewController(progress: progress)
        navigationController?.pushViewController(view, animated: true)
    }

    func deviceTransferServiceDidEndTransfer(error: DeviceTransferService.Error?) {
        if let error = error {
            owsFailDebug("unexpected error while rendering QR code \(error)")
        }
    }

    func deviceTransferServiceDidRequestAppRelaunch() {
        if CurrentAppContext().frontmostViewController() == self {
            owsFail("Relaunch not supported from QR screen")
        }
    }
}
