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

import SignalServiceKit
import SignalUI

protocol RegistrationPhoneNumberInputViewDelegate: AnyObject {
    func present(_ countryCodeViewController: CountryCodeViewController)
    func didChange()
    func didPressReturn()
}

class RegistrationPhoneNumberInputView: UIStackView {
    public weak var delegate: RegistrationPhoneNumberInputViewDelegate?

    // We impose a limit on the number of digits. This is much higher than what a valid E164 allows
    // and is just here for safety.
    private let maxNationalNumberDigits = 50

    init(initialPhoneNumber: RegistrationPhoneNumber) {
        self.countryState = initialPhoneNumber.countryState

        super.init(frame: .zero)

        axis = .horizontal
        distribution = .fillProportionally
        spacing = 16
        layoutMargins = .init(hMargin: 16, vMargin: 14)
        isLayoutMarginsRelativeArrangement = true
        autoSetDimension(.height, toSize: 50, relation: .greaterThanOrEqual)

        insertSubview(backgroundView, at: 0)
        backgroundView.autoPinEdgesToSuperviewEdges()

        addArrangedSubview(countryCodeView)

        addArrangedSubview(dividerView)

        nationalNumberView.text = initialPhoneNumber.nationalNumber
        addArrangedSubview(nationalNumberView)

        render()
    }

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

    // MARK: - Data

    public private(set) var countryState: RegistrationCountryState {
        didSet { render() }
    }

    public var nationalNumber: String { nationalNumberView.text?.asciiDigitsOnly ?? "" }

    public var e164: E164? {
        return RegistrationPhoneNumber(
            countryState: countryState,
            nationalNumber: nationalNumber
        ).e164
    }

    public var isEnabled: Bool = true {
        didSet {
            if !isEnabled {
                nationalNumberView.resignFirstResponder()
            }
            render()
        }
    }

    // MARK: - Rendering

    private let backgroundView: UIView = {
        let result = UIView()
        result.layer.cornerRadius = 10
        return result
    }()

    private lazy var countryCodeLabel: UILabel = {
        let result = UILabel()
        result.font = .dynamicTypeBody
        result.textAlignment = .center
        result.setCompressionResistanceHigh()
        result.setContentHuggingHorizontalHigh()
        return result
    }()

    private lazy var countryCodeChevron: UIImageView = {
        let result = UIImageView(image: UIImage(imageLiteralResourceName: "chevron-down-extra-small"))
        result.autoSetDimensions(to: .square(12))
        result.setCompressionResistanceHigh()
        return result
    }()

    private lazy var countryCodeView: UIView = {
        let result = UIStackView(arrangedSubviews: [countryCodeLabel, countryCodeChevron])
        result.distribution = .fill
        result.alignment = .center
        result.spacing = 9
        result.setCompressionResistanceHigh()
        result.setContentHuggingHorizontalHigh()
        result.accessibilityIdentifier = "registration.phonenumber.countryCode"

        result.isUserInteractionEnabled = true
        result.addGestureRecognizer(UITapGestureRecognizer(
            target: self,
            action: #selector(didTapCountryCode)
        ))

        return result
    }()

    private let dividerView: UIView = {
        let result = UIView()
        result.autoSetDimension(.width, toSize: .hairlineWidth)
        result.setContentHuggingHorizontalHigh()
        return result
    }()

    private lazy var nationalNumberView: UITextField = {
        let result = UITextField()
        result.font = UIFont.dynamicTypeBodyClamped
        result.textAlignment = .left
        result.textContentType = .telephoneNumber
        result.keyboardType = .phonePad
        result.placeholder = OWSLocalizedString(
            "ONBOARDING_PHONE_NUMBER_PLACEHOLDER",
            comment: "Placeholder string for phone number field during registration"
        )
        result.accessibilityIdentifier = "registration.phonenumber.phoneNumberTextField"

        result.delegate = self

        result.addTarget(delegate, action: #selector(didChange), for: .valueChanged)

        return result
    }()

    public func render() {
        backgroundView.backgroundColor = Theme.secondaryBackgroundColor

        countryCodeLabel.textColor = Theme.primaryTextColor
        countryCodeChevron.tintColor = Theme.primaryIconColor
        dividerView.backgroundColor = Theme.primaryIconColor
        nationalNumberView.textColor = Theme.primaryTextColor

        countryCodeLabel.text = countryState.callingCode
        nationalNumberView.isEnabled = isEnabled
    }

    // MARK: - Events

    @objc
    private func didTapCountryCode(sender: UIGestureRecognizer) {
        guard isEnabled, sender.state == .recognized, let delegate else { return }

        let countryCodeViewController = CountryCodeViewController()
        countryCodeViewController.countryCodeDelegate = self
        countryCodeViewController.interfaceOrientationMask = UIDevice.current.isIPad ? .all : .portrait

        delegate.present(countryCodeViewController)
    }

    // MARK: - Responder pass-through

    public override var isFirstResponder: Bool { nationalNumberView.isFirstResponder }

    public override var canBecomeFirstResponder: Bool { nationalNumberView.canBecomeFirstResponder }

    @discardableResult
    public override func becomeFirstResponder() -> Bool { nationalNumberView.becomeFirstResponder() }

    @discardableResult
    public override func resignFirstResponder() -> Bool { nationalNumberView.resignFirstResponder() }
}

// MARK: - UITextFieldDelegate

extension RegistrationPhoneNumberInputView: UITextFieldDelegate {
    func textField(
        _ textField: UITextField,
        shouldChangeCharactersIn range: NSRange,
        replacementString: String
    ) -> Bool {
        let wasEmpty = textField.text.isEmptyOrNil
        var replacementString = replacementString

        if
            textField.text.isEmptyOrNil,
            let fulle164 = E164(replacementString.removeCharacters(characterSet: CharacterSet(charactersIn: " -()"))),
            let phoneNumber = RegistrationPhoneNumberParser(phoneNumberUtil: phoneNumberUtil).parseE164(fulle164)
        {
            // If we got a full e164, it was probably from system autofill.
            // Split out the country code portion.
            self.countryState = phoneNumber.countryState
            replacementString = phoneNumber.nationalNumber
        }

        let result = FormattedNumberField.textField(
            textField,
            shouldChangeCharactersIn: range,
            replacementString: replacementString,
            allowedCharacters: .numbers,
            maxCharacters: maxNationalNumberDigits,
            format: formatNationalNumber
        )

        if wasEmpty {
            DispatchQueue.main.async {
                // Move the cursor back to the end.
                textField.selectedTextRange = textField.textRange(
                    from: textField.endOfDocument,
                    to: textField.endOfDocument
                )
            }
        }

        return result
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        delegate?.didPressReturn()
        return false
    }

    private func formatNationalNumber(input: String) -> String {
        return PhoneNumber.bestEffortFormatPartialUserSpecifiedText(
            toLookLikeAPhoneNumber: input,
            withSpecifiedCountryCodeString: countryState.callingCode
        )
    }
}

// MARK: - CountryCodeViewControllerDelegate

extension RegistrationPhoneNumberInputView: CountryCodeViewControllerDelegate {
    func countryCodeViewController(
        _ vc: CountryCodeViewController,
        didSelectCountry newCountryState: RegistrationCountryState
    ) {
        countryState = newCountryState

        nationalNumberView.text = PhoneNumber.bestEffortFormatPartialUserSpecifiedText(
            toLookLikeAPhoneNumber: nationalNumber,
            withSpecifiedCountryCodeString: countryState.callingCode
        )

        delegate?.didChange()
    }
}
