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

import Foundation
import SignalUI
import SignalServiceKit

public enum SafetyTipsType {
    case contact
    case group
}

public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollViewDelegate {
    let contentScrollView = UIScrollView()
    let stackView = UIStackView()
    public override var interactiveScrollViews: [UIScrollView] { [contentScrollView] }
    public override var sheetBackgroundColor: UIColor { Theme.tableView2PresentedBackgroundColor }

    private enum Constants {
        static let stackSpacing: CGFloat = 12.0

        static let outerSpacing: CGFloat = 20.0
        static let outerMargins: UIEdgeInsets = .init(hMargin: 24.0, vMargin: 0.0)

        static let footerSpacing: CGFloat = 16.0
        static let footerMargins: UIEdgeInsets = .init(
            top: 0.0,
            left: 24.0,
            bottom: 42.0,
            right: 24.0
        )

        static let buttonInsets: UIEdgeInsets = .init(
            top: 16.0,
            leading: 36.0,
            bottom: 12.0,
            trailing: 36.0
        )

        static let buttonEdgeInsets: UIEdgeInsets = .init(
            hMargin: 0.0,
            vMargin: 14.0
        )
    }

    fileprivate enum SafetyTips: CaseIterable {
        case crypto
        case vagueMessages
        case messagesWithLinks
        case fakeBusiness

        var image: UIImage? {
            switch self {
            case .crypto:
                return UIImage(named: "safety-tip-1")
            case .vagueMessages:
                return UIImage(named: "safety-tip-2")
            case .messagesWithLinks:
                return UIImage(named: "safety-tip-3")
            case .fakeBusiness:
                return UIImage(named: "safety-tip-4")
            }
        }

        var title: String {
            switch self {
            case .crypto:
                return OWSLocalizedString(
                    "SAFETY_TIPS_CRYPTO_TITLE",
                    comment: "Message title describing the crypto safety tip."
                )
            case .vagueMessages:
                return OWSLocalizedString(
                    "SAFETY_TIPS_VAGUE_MESSAGE_TITLE",
                    comment: "Message title describing the safety tip about vague messages."
                )
            case .messagesWithLinks:
                return OWSLocalizedString(
                    "SAFETY_TIPS_MESSAGE_LINKS_TITLE",
                    comment: "Message title describing the safety tip about unknown links in messages."
                )
            case .fakeBusiness:
                return OWSLocalizedString(
                    "SAFETY_TIPS_FAKE_BUSINESS_TITLE",
                    comment: "Message title describing the safety tip about unknown or fake businesses."
                )
            }
        }

        var body: String {
            switch self {
            case .crypto:
                return OWSLocalizedString(
                    "SAFETY_TIPS_CRYPTO_BODY",
                    comment: "Message contents for the crypto safety tip."
                )
            case .vagueMessages:
                return OWSLocalizedString(
                    "SAFETY_TIPS_VAGUE_MESSAGE_BODY",
                    comment: "Message contents for the vague message safety tip."
                )
            case .messagesWithLinks:
                return OWSLocalizedString(
                    "SAFETY_TIPS_MESSAGE_LINKS_BODY",
                    comment: "Message contents for the unknown links in messages safety tip."
                )
            case .fakeBusiness:
                return OWSLocalizedString(
                    "SAFETY_TIPS_FAKE_BUSINESS_BODY",
                    comment: "Message contents for the safety tip concerning fake businesses."
                )
            }
        }
    }

    public var prefersNavigationBarHidden: Bool { true }

    private let type: SafetyTipsType

    init(type: SafetyTipsType) {
        self.type = type
        super.init()
    }

    public override func viewDidLoad() {

        minimizedHeight = min(725, CurrentAppContext().frame.height)
        super.allowsExpansion = false

        contentView.addSubview(contentScrollView)
        contentScrollView.addSubview(stackView)

        stackView.axis = .vertical
        stackView.spacing = Constants.outerSpacing
        stackView.isLayoutMarginsRelativeArrangement = true
        stackView.autoPinEdgesToSuperviewEdges()
        stackView.autoPinWidth(toWidthOf: contentScrollView)
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.autoPinEdge(.bottom, to: .bottom, of: contentView, withOffset: 0.0, relation: .greaterThanOrEqual)

        contentScrollView.translatesAutoresizingMaskIntoConstraints = false
        contentScrollView.autoPinEdgesToSuperviewEdges()

        buildContents()
        updateButtonState()
        setColorsForCurrentTheme()
        super.viewDidLoad()
    }

    public override func themeDidChange() {
        super.themeDidChange()
        buildContents()
        updateButtonState()
        setColorsForCurrentTheme()
    }

    // MARK: - Views

    private lazy var tipScrollView: UIScrollView = {
        let scrollView = UIScrollView(frame: .zero)
        scrollView.isPagingEnabled = true
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.delegate = self
        return scrollView
    }()

    private lazy var pageControl: UIPageControl = {
        let pageControl = UIPageControl()
        pageControl.numberOfPages = SafetyTips.allCases.count
        pageControl.currentPage = 0
        pageControl.addTarget(self, action: #selector(self.changePage), for: .valueChanged)
        return pageControl
    }()

    private lazy var previousTipButton: OWSFlatButton = {
        let previousButton = OWSFlatButton.insetButton(
            title: OWSLocalizedString(
                "SAFETY_TIPS_PREVIOUS_TIP_BUTTON",
                comment: "Button that will show the previous safety tip."
            ),
            font: .dynamicTypeBodyClamped.semibold(),
            titleColor: .white,
            backgroundColor: .ows_accentBlue,
            target: self,
            selector: #selector(didTapPrevious))
        previousButton.button.setBackgroundImage(UIImage.image(color: .clear), for: .disabled)
        previousButton.button.setTitleColor(.ows_accentBlue, for: .disabled)
        previousButton.contentEdgeInsets = Constants.buttonEdgeInsets
        return previousButton
    }()

    private lazy var nextTipButton: OWSFlatButton = {
        let nextButton = OWSFlatButton.insetButton(
            title: OWSLocalizedString(
                "SAFETY_TIPS_NEXT_TIP_BUTTON",
                comment: "Button that will show the next safety tip."
            ),
            font: .dynamicTypeBodyClamped.semibold(),
            titleColor: .white,
            backgroundColor: .ows_accentBlue,
            target: self,
            selector: #selector(didTapNext))
        nextButton.button.setBackgroundImage(UIImage.image(color: .clear), for: .disabled)
        nextButton.button.setTitleColor(.ows_accentBlue, for: .disabled)
        nextButton.contentEdgeInsets = Constants.buttonEdgeInsets
        return nextButton
    }()

    private lazy var footerView: UIView = {
        let stackView = UIStackView(arrangedSubviews: [
            previousTipButton,
            nextTipButton,
        ])
        let container = UIView()
        container.addSubview(stackView)
        container.layoutMargins = Constants.footerMargins
        container.setContentHuggingHigh()

        stackView.axis = .horizontal
        stackView.spacing = Constants.footerSpacing
        stackView.distribution = .fillEqually
        stackView.autoPinEdgesToSuperviewMargins()

        return container
    }()

    // MARK: - TableView

    private func buildContents() {
        prepareTipsScrollView()
        stackView.removeAllSubviews()
        stackView.addArrangedSubview(Self.HeaderView(type: type))
        stackView.addArrangedSubview(tipScrollView)
        stackView.setCustomSpacing(8.0, after: tipScrollView)
        stackView.addArrangedSubview(pageControl)
        stackView.setCustomSpacing(0.0, after: pageControl)
        stackView.addArrangedSubview(UIView.transparentSpacer())
        stackView.addArrangedSubview(footerView)
    }

    private func prepareTipsScrollView() {
        var priorView: UIView?
        tipScrollView.removeAllSubviews()
        SafetyTips.allCases.forEach { tip in
            let view = SafetyTipView(safetyTip: tip)
            tipScrollView.addSubview(view)

            view.autoPinHeight(toHeightOf: tipScrollView)
            view.autoPinWidth(toWidthOf: tipScrollView)
            if let priorView {
                view.autoPinEdge(.leading, to: .trailing, of: priorView)
            } else {
                view.autoPinEdge(.leading, to: .leading, of: tipScrollView)
            }
            priorView = view
        }
        priorView?.autoPinEdge(.trailing, to: .trailing, of: tipScrollView)
    }

    // MARK: - ScrollViewDelegate

    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
        pageControl.currentPage = Int(pageNumber)
        updateButtonState()
    }

    // MARK: - Actions

    @objc
    private func changePage() {
        let x = CGFloat(pageControl.currentPage) * tipScrollView.frame.size.width
        tipScrollView.setContentOffset(CGPoint(x: x, y: 0), animated: true)
        updateButtonState()
    }

    @objc
    private func didTapPrevious() {
        pageControl.currentPage = max(pageControl.currentPage - 1, 0)
        changePage()
    }

    @objc
    private func didTapNext() {
        pageControl.currentPage = min(pageControl.currentPage + 1, pageControl.numberOfPages)
        changePage()
    }

    private func updateButtonState() {
        switch pageControl.currentPage {
        case 0:
            previousTipButton.setEnabled(false)
            nextTipButton.setEnabled(true)
        case pageControl.numberOfPages - 1:
            previousTipButton.setEnabled(true)
            nextTipButton.setEnabled(false)
        default:
            previousTipButton.setEnabled(true)
            nextTipButton.setEnabled(true)
        }
    }

    private func setColorsForCurrentTheme() {
        pageControl.pageIndicatorTintColor = Theme.isDarkThemeEnabled ? .ows_gray65 : .ows_gray20
        pageControl.currentPageIndicatorTintColor = Theme.isDarkThemeEnabled ? .ows_gray20 : .ows_gray65
    }
}

extension SafetyTipsViewController {
    class HeaderView: UIView {

        private let type: SafetyTipsType

        // MARK: Init

        init(type: SafetyTipsType) {
            self.type = type
            super.init(frame: .zero)
            layoutMargins = Constants.outerMargins

            let stackView = UIStackView()
            self.addSubview(stackView)

            stackView.axis = .vertical
            stackView.alignment = .center
            stackView.spacing = Constants.stackSpacing
            stackView.autoPinEdgesToSuperviewMargins()
            stackView.addArrangedSubviews([
                titleLabel,
                subtitleLabel
            ])

            self.setContentHuggingHigh()
            updateFontsForCurrentPreferredContentSize()
            setColorsForCurrentTheme()
        }

        @available(*, unavailable, message: "Use other constructor")
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }

        // MARK: Views

        private lazy var titleLabel: UILabel = {
            let label = UILabel()
            label.text = OWSLocalizedString(
                "SAFETY_TIPS_HEADER_TITLE",
                comment: "Title for Safety Tips education screen.")
            label.numberOfLines = 0
            label.textAlignment = .center
            label.lineBreakMode = .byWordWrapping
            label.setContentHuggingVerticalHigh()
            return label
        }()

        private lazy var subtitleLabel: UILabel = {
            let label = UILabel()
            let message = {
                switch type {
                case .contact:
                    return OWSLocalizedString(
                        "SAFETY_TIPS_INDIVIDUAL_HEADER_MESSAGE",
                        comment: "Message describing safety tips for 1:1 conversations."
                    )
                case .group:
                    return OWSLocalizedString(
                        "SAFETY_TIPS_GROUPS_HEADER_MESSAGE",
                        comment: "Message describing safety tips for group conversations."
                    )
                }
            }()
            label.text = message
            label.numberOfLines = 0
            label.textAlignment = .center
            label.lineBreakMode = .byWordWrapping
            label.setContentHuggingVerticalHigh()
            return label
        }()

        // MARK: - Style views

        func updateFontsForCurrentPreferredContentSize() {
            titleLabel.font = .dynamicTypeTitle2Clamped.bold()
            subtitleLabel.font = .dynamicTypeBody2Clamped
        }

        func setColorsForCurrentTheme() {
            titleLabel.textColor = Theme.primaryTextColor
            subtitleLabel.textColor = Theme.primaryTextColor
        }
    }
}

extension SafetyTipsViewController {
    class SafetyTipView: UIView {
        private enum Constants {
            static let cornerRadius: CGFloat = 20.0
            static let layoutMargin: CGFloat = 12.0
            static let imageMargin: CGFloat = 24.0
            static let containerMargins: UIEdgeInsets = .init(
                top: layoutMargin,
                left: layoutMargin,
                bottom: 24.0,
                right: layoutMargin
            )
        }

        fileprivate init(safetyTip: SafetyTips) {
            super.init(frame: .zero)
            layoutMargins = .init(hMargin: 24.0, vMargin: 0.0)

            let containerView = UIView()
            containerView.layer.cornerRadius = Constants.cornerRadius
            containerView.backgroundColor = Theme.isDarkThemeEnabled ? .ows_gray75 : .ows_white
            self.addSubview(containerView)
            containerView.layoutMargins = Constants.containerMargins
            containerView.autoPinEdgesToSuperviewMargins()

            let stackView = UIStackView()
            stackView.axis = .vertical
            containerView.addSubview(stackView)
            stackView.spacing = Constants.layoutMargin
            stackView.autoPinEdgesToSuperviewMargins()

            let imageContainerView = UIView()
            imageContainerView.backgroundColor = Theme.isDarkThemeEnabled ? .ows_gray60 : .ows_gray02
            imageContainerView.layer.cornerRadius = Constants.cornerRadius
            imageContainerView.layoutMargins = .init(margin: Constants.imageMargin)
            stackView.addArrangedSubview(imageContainerView)

            let imageView = UIImageView(image: safetyTip.image)
            imageView.contentMode = .scaleAspectFit

            imageContainerView.addSubview(imageView)
            imageView.autoPinEdgesToSuperviewMargins()
            imageView.autoPinToAspectRatio(withSize: safetyTip.image?.size ?? .init(square: 1.0))

            let titleLabel = UILabel()
            titleLabel.text = safetyTip.title
            titleLabel.numberOfLines = 0
            titleLabel.textAlignment = .center
            titleLabel.lineBreakMode = .byWordWrapping
            titleLabel.font = .dynamicTypeBody.bold()
            titleLabel.textColor = Theme.primaryTextColor
            stackView.addArrangedSubview(titleLabel)

            let messageLabel = UILabel()
            messageLabel.text = safetyTip.body
            messageLabel.numberOfLines = 0
            messageLabel.textAlignment = .center
            messageLabel.lineBreakMode = .byWordWrapping
            messageLabel.font = .dynamicTypeBodyClamped
            messageLabel.textColor = Theme.secondaryTextAndIconColor
            stackView.addArrangedSubview(messageLabel)
        }

        @available(*, unavailable, message: "Use other constructor")
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
}
