// Copyright © 2022 Olo Inc. All rights reserved.
// This software is made available under the Olo Pay SDK License (See LICENSE.md file)

import UIKit
import OloPaySDK

// Internal delegate adapter to avoid exposing OloPaySDK protocol conformance in bridging header
private class PaymentCardDetailsFormDelegateAdapter: NSObject, OPPaymentCardDetailsFormDelegate {
    weak var fabricView: PaymentCardDetailsForm?

    func isValidChanged(_ form: OPPaymentCardDetailsForm, _ isValid: Bool) {
        fabricView?.handleIsValidChanged(isValid)
    }
}

@objc(PaymentCardDetailsForm)
public class PaymentCardDetailsForm: UIView {
    private var formView: OPPaymentCardDetailsForm?
    private var delegateAdapter: PaymentCardDetailsFormDelegateAdapter?

    @objc public var onFormInputChange: ((NSDictionary) -> Void)?
    @objc public var onFormFocusChange: ((NSDictionary) -> Void)?
    @objc public var onPaymentMethodResult: ((NSDictionary) -> Void)?

    private var currentFocusedField: String = ""
    private var _cachedIsValid: Bool = false

    @objc public var isEnabled: Bool = true {
        didSet {
            formView?.isUserInteractionEnabled = isEnabled
        }
    }

    public override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupView()
    }

    private func setupView() {
        let view = OPPaymentCardDetailsForm()
        view.translatesAutoresizingMaskIntoConstraints = false

        // Create and set up delegate adapter
        let adapter = PaymentCardDetailsFormDelegateAdapter()
        adapter.fabricView = self
        delegateAdapter = adapter
        view.cardDetailsDelegate = adapter

        addSubview(view)
        NSLayoutConstraint.activate([
            view.leadingAnchor.constraint(equalTo: leadingAnchor),
            view.trailingAnchor.constraint(equalTo: trailingAnchor),
            view.topAnchor.constraint(equalTo: topAnchor),
            view.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])

        formView = view
    }

    @objc public func updateFieldStyles(_ styles: NSDictionary) {
        guard let formView = formView else { return }

        if let backgroundColor = styles[DataKeys.BackgroundColorKey] as? String {
            formView.backgroundColor = UIColor(hex: backgroundColor)
        }
        if let cursorColor = styles[DataKeys.CursorColorKey] as? String {
            formView.tintColor = UIColor(hex: cursorColor)
        }
    }

    @objc public func updatePlaceholders(_ placeholders: NSDictionary) {
        // OPPaymentCardDetailsForm doesn't support custom placeholders
        // This is a no-op but required for the Fabric interface
    }

    @objc public func focus() {
        // Default: focus the card number field
        formView?.becomeFirstResponder(at: .number)
    }

    @objc public func focusField(_ field: String) {
        guard let formView = formView else { return }

        switch field {
        case DataKeys.CardNumberFieldKey:
            formView.becomeFirstResponder(at: .number)
        case DataKeys.ExpirationFieldKey:
            formView.becomeFirstResponder(at: .expiration)
        case DataKeys.CvvFieldKey:
            formView.becomeFirstResponder(at: .cvv)
        case DataKeys.PostalCodeFieldKey:
            formView.becomeFirstResponder(at: .postalCode)
        default:
            break
        }
    }

    @objc public func blur() {
        formView?.resignFirstResponder()
    }

    @objc public func clear() {
        guard let formView = formView else { return }
        clearTextFields(in: formView)
        formView.becomeFirstResponder(at: .number)
    }

    private func clearTextFields(in view: UIView) {
        for subview in view.subviews {
            if let textField = subview as? UITextField {
                textField.text = ""
                textField.sendActions(for: .editingChanged)
            }
            clearTextFields(in: subview)
        }
    }

    @objc public func createPaymentMethod() {
        guard let formView = formView else { return }

        guard let params = formView.getPaymentMethodParams() else {
            // Emit result with error - use NSString explicitly for proper bridging
            let errorDict: NSDictionary = [
                DataKeys.MessageKey as NSString: "Card details are invalid" as NSString,
                DataKeys.CodeKey as NSString: ErrorCodes.InvalidCardDetails as NSString
            ]
            let resultDict: NSDictionary = [DataKeys.ErrorKey as NSString: errorDict]
            onPaymentMethodResult?(resultDict)
            return
        }

        OloPayAPI().createPaymentMethod(with: params) { [weak self] paymentMethod, error in
            DispatchQueue.main.async {
                if let error = error {
                    // Emit result with error
                    let errorDict: NSDictionary = [
                        DataKeys.MessageKey as NSString: error.localizedDescription as NSString,
                        DataKeys.CodeKey as NSString: getErrorCode(error) as NSString
                    ]
                    let resultDict: NSDictionary = [DataKeys.ErrorKey as NSString: errorDict]
                    self?.onPaymentMethodResult?(resultDict)
                    return
                }

                guard let paymentMethod = paymentMethod else {
                    // Emit result with error
                    let errorDict: NSDictionary = [
                        DataKeys.MessageKey as NSString: "Unexpected error occurred" as NSString,
                        DataKeys.CodeKey as NSString: ErrorCodes.GeneralError as NSString
                    ]
                    let resultDict: NSDictionary = [DataKeys.ErrorKey as NSString: errorDict]
                    self?.onPaymentMethodResult?(resultDict)
                    return
                }

                // Emit result with success
                let resultDict: NSDictionary = [DataKeys.PaymentMethodKey as NSString: paymentMethod.toDictionary()]
                self?.onPaymentMethodResult?(resultDict)
            }
        }
    }

    // MARK: - Internal delegate callbacks (called by adapter)

    func handleIsValidChanged(_ isValid: Bool) {
        guard isValid != _cachedIsValid else { return }
        _cachedIsValid = isValid
        emitFormInputChangeEvent(isValid)
    }

    private func emitFormInputChangeEvent(_ isValid: Bool) {
        let eventData: [String: Any] = [DataKeys.IsValidKey: isValid]
        onFormInputChange?(eventData as NSDictionary)
    }

    private func emitFocusChangeEvent(_ focusedField: String) {
        let state = formView?.isValid == true ? DataKeys.ValidStateValue : DataKeys.InvalidStateValue
        let eventData: [String: Any] = [
            DataKeys.StateKey: state,
            DataKeys.FocusedFieldKey: focusedField
        ]

        onFormFocusChange?(eventData as NSDictionary)
    }
}
