// PersonaInquiryViewManager.swift
// PersonaInquiry
//
// Copyright © 2025 Persona. All rights reserved.


import Foundation
import Persona2

#if canImport(PersonaNfc)
import PersonaNfc
#endif

#if canImport(PersonaSna)
import PersonaSna
#endif

#if canImport(PersonaWebRtc)
import PersonaWebRtc
#endif

final class PersonaInquiryView: UIView {
    @objc var inquiry: NSDictionary? {
        didSet {
            manager?.createInlineInquiry(view: self)
        }
    }
    @objc var onComplete: RCTDirectEventBlock?
    @objc var onCanceled: RCTDirectEventBlock?
    @objc var onError: RCTDirectEventBlock?
    @objc var onEvent: RCTDirectEventBlock?
    @objc var onReady: RCTDirectEventBlock?

    weak var manager: PersonaInquiryViewManager?
}

// file private for this variable avoids clang module linker bug
private var collectedData: InquiryData? = nil

@objc(PersonaInquiryView)
final class PersonaInquiryViewManager: RCTViewManager {
    
    private var inquiryView: PersonaInquiryView?
    private var inlineViewController: PersonaInlineViewController?

    override func view() -> UIView! {
        let view = PersonaInquiryView()
        view.clipsToBounds = true
        view.manager = self
        inquiryView = view
        return view
    }

    override static func requiresMainQueueSetup() -> Bool {
        return true
    }

    override func constantsToExport() -> [AnyHashable : Any]! {
        return ["test": "PersonaInquiryViewManager loaded"]
    }

    @objc
    func create(_ reactTag: NSNumber, withViewId viewId: NSNumber) {

    }

    @objc
    func createInlineInquiry(view: PersonaInquiryView) {
        DispatchQueue.main.async {
            guard let options = view.inquiry else {
                print("No config")
                return
            }

            self.createInquiry(for: view, options: options)
        }
    }

    private func createInquiry(
        for inquiryView: PersonaInquiryView,
        options: NSDictionary
    ) {
        let templateId = options["templateId"] as? String
        let templateVersion = options["templateVersion"] as? String
        let inquiryId = options["inquiryId"] as? String
        let referenceId = options["referenceId"] as? String
        let accountId = options["accountId"] as? String
        let environment = options["environment"] as? String
        let environmentId = options["environmentId"] as? String
        let sessionToken = options["sessionToken"] as? String
        let shareToken = options["shareToken"] as? String
        let themeObject = options["iosTheme"] as? [String: String]
        let themeSource = options["themeSource"] as? String
        let fieldsObject = options["fields"] as? [String: [String: Any]]
        let returnCollectedData = options["returnCollectedData"] as? Bool ?? false
        // our iOS SDK uses Locale.current.identifier which chooses app language instead of device language
        // imo this is the appropriate level of control, that way apps can enforce what languages they support
        // and not be beholden to our SDK using a device language they don't want or expect
        // react native apps are dumb and don't configure app languages correctly on iOS
        // so we'll just use the preferred device language as that is the frequent expectation from this platform
        let locale = options["locale"] as? String ?? Locale.preferredLanguages.first
        let styleVariantString = options["styleVariant"] as? String
        let styleVariant = StyleVariant.from(rawValue: styleVariantString)
        let themeSetId = options["themeSetId"] as? String
        let disablePresentationAnimation = options["disablePresentationAnimation"] as? Bool ?? false

        var nfcAdapter: InquiryNfcAdapter? = nil
        #if canImport(PersonaNfc)
        nfcAdapter = PersonaNfcAdapter()
        #endif

        var snaAdapter: InquirySnaAdapter? = nil
        #if canImport(PersonaSna)
        snaAdapter = PersonaSnaAdapter()
            #endif

        var webRtcAdapter: InquiryWebRtcAdapter? = nil
        #if canImport(PersonaWebRtc)
        webRtcAdapter = PersonaWebRtcAdapter()
        #endif

        var inquiry: Inquiry? = nil
        if let inquiryId {
            let builder = Inquiry.from(inquiryId: inquiryId, delegate: self)

            builder
                .sessionToken(sessionToken)
                .shareToken(shareToken)
                .theme(PersonaInquiry2.makeTheme(from: themeObject, themeSource: themeSource))
                .nfcAdapter(nfcAdapter)
                .snaAdapter(snaAdapter)
                .collectionDelegate(returnCollectedData ? self : nil)
                .locale(locale)
                .styleVariant(styleVariant)

            if let webRtcAdapter {
                builder.webRtcAdapter(webRtcAdapter)
            }
            inquiry = builder.build()
        } else if let templateId {
            let builder = Inquiry.from(templateId: templateId, delegate: self)
            builder
                .referenceId(referenceId)
                .accountId(accountId)
                .environment(Environment.from(rawValue: environment))
                .environmentId(environmentId)
                .fields(PersonaInquiry2.fieldsFrom(fieldsObject))
                .theme(PersonaInquiry2.makeTheme(from: themeObject, themeSource: themeSource))
                .themeSetId(themeSetId)
                .nfcAdapter(nfcAdapter)
                .snaAdapter(snaAdapter)
                .collectionDelegate(returnCollectedData ? self : nil)
                .locale(locale)
                .styleVariant(styleVariant)
                .shareToken(shareToken)

            if let webRtcAdapter {
                builder.webRtcAdapter(webRtcAdapter)
            }

            inquiry = builder.build()
        } else if let templateVersion {
            let builder = Inquiry.from(templateVersion: templateVersion, delegate: self)
            builder
                .referenceId(referenceId)
                .accountId(accountId)
                .environment(Environment.from(rawValue: environment))
                .environmentId(environmentId)
                .fields(PersonaInquiry2.fieldsFrom(fieldsObject))
                .theme(PersonaInquiry2.makeTheme(from: themeObject, themeSource: themeSource))
                .themeSetId(themeSetId)
                .nfcAdapter(nfcAdapter)
                .snaAdapter(snaAdapter)
                .collectionDelegate(returnCollectedData ? self : nil)
                .locale(locale)
                .styleVariant(styleVariant)
                .shareToken(shareToken)

            if let webRtcAdapter {
                builder.webRtcAdapter(webRtcAdapter)
            }

            inquiry = builder.build()
        }

        guard let inquiry, let viewController = inquiry.startInline() else {
            print("An error occurred while starting the Inquiry, invalid config.")
            return
        }

        viewController.isPersonaNavigationHidden = false
        viewController.delegate = self

        let reactViewController = inquiryView.reactViewController()
        viewController.willMove(toParent: reactViewController)
        reactViewController?.addChild(viewController)

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

        viewController.didMove(toParent: reactViewController)
        inlineViewController = viewController
    }
}

// MARK: - InquiryDelegate

extension PersonaInquiryViewManager: InquiryDelegate {
    func inquiryComplete(inquiryId: String, status: String, fields: [String: InquiryField]) {
        guard let inquiryView else {
            return
        }
        
        inquiryView.onComplete?([
            "inquiryId": inquiryId,
            "status": status,
            "fields": PersonaInquiry2.fieldsToDictionary(fields: fields),
        ])
    }

    func inquiryCanceled(inquiryId: String?, sessionToken: String?) {
        guard let inquiryView else { return }

        inquiryView.onCanceled?([
            "inquiryId": inquiryId,
            "sessionToken": sessionToken
        ])
    }

    func inquiryError(_ error: Error) {
        guard let inquiryView else { return }

        inquiryView.onError?([
            "debugMessage": error.localizedDescription
        ])
    }

    func inquiryEventOccurred(event: InquiryEvent) {
        let canProcessEvent = switch event {
        case .start, .pageChange:
            true
        @unknown default:
            false
        }
        guard canProcessEvent else { return }
        guard let inquiryView else { return }

        inquiryView.onEvent?([
            "event": inquiryEventToDictionary(event: event)
        ])
    }
}

// MARK: - InquiryCollectionDelegate

extension PersonaInquiryViewManager: InquiryCollectionDelegate {

    func collectionComplete(data: InquiryData) {
        collectedData = data
    }
}

extension PersonaInquiryViewManager: PersonaInlineDelegate {

    func navigationStateDidUpdate(navigationState: PersonaInlineNavigationState) {
        // TODO: add custom navigation support
    }

    func onReady() {
        guard let inquiryView else { return }

        inquiryView.onReady?([:])
    }
}

// MARK: - Event Helpers

extension PersonaInquiryViewManager {
    /// Converts an InquiryEvent like
    ///     `.pageChange(name: "start", path: "inquriy/start")]` to
    ///     `["name": "start", "path": "inquriy/start"]`
    private func inquiryEventToDictionary(event: InquiryEvent) -> [String: String] {
        switch event {
            case .start(let value):
                return [
                    "type": "start",
                    "inquiryId": value.inquiryId,
                    "sessionToken": value.sessionToken,
                ]
            case .pageChange(let value):
                return [
                    "type": "page_change",
                    "name": value.name,
                    "path": value.path,
                ]
            @unknown default:
                return [
                    "type": "unknown",
                    "value": "unknown"
                ]
        }
    }
}
