/**
 * INTEGRATION TEST: WebView Communication
 *
 * CRITICAL: Tests the core WebView message passing bridge between
 * the embedded widget and React Native.
 *
 * Coverage Target: handleWebViewMessage, handleWebViewLoad, handleClose
 * Previous Coverage: 0% (CRITICAL GAP)
 *
 * Strategy: Minimal mocking - use real WidgetEventService and components
 */

import React from "react";
import { render, waitFor, fireEvent } from "@testing-library/react-native";
import { SoluCXWidgetView as SoluCXWidget } from "../../SoluCXWidgetView";
import type { WidgetCallbacks } from "../../domain";

// Mock only external dependencies we can't control
jest.mock("react-native-webview", () => {
    const React = require("react");
    const { View } = require("react-native");
    return {
        __esModule: true,
        WebView: React.forwardRef((props: any, ref: any) => {
            // Store ref methods for testing
            React.useImperativeHandle(ref, () => ({
                injectJavaScript: jest.fn(script => {
                    // Simulate successful injection
                    if (props.onLoadEnd) props.onLoadEnd();
                }),
            }));

            return (
                <View testID="webview" {...props}>
                    {/* Expose onMessage handler for testing */}
                    {props.children}
                </View>
            );
        }),
    };
});

jest.mock("../../services/WidgetBootstrapService", () => ({
    requestWidgetUrl: jest.fn().mockResolvedValue({ available: true, url: "https://mock.widget.url/form123" }),
}));

jest.mock("../../services/WidgetValidationService", () => ({
    WidgetValidationService: jest.fn().mockImplementation(() => ({
        shouldDisplayForTransaction: jest.fn().mockResolvedValue({ canDisplay: true }),
        shouldDisplayForTransactionAlreadyAnswered: jest.fn().mockResolvedValue({ canDisplay: true }),
        shouldDisplayWidget: jest.fn().mockResolvedValue({ canDisplay: true }),
    })),
}));

// Mock storage but keep it functional
const mockStorage: Record<string, string> = {};
jest.mock("@react-native-async-storage/async-storage", () => ({
    __esModule: true,
    default: {
        getItem: jest.fn((key: string) => Promise.resolve(mockStorage[key] || null)),
        setItem: jest.fn((key: string, value: string) => {
            mockStorage[key] = value;
            return Promise.resolve();
        }),
        removeItem: jest.fn((key: string) => {
            delete mockStorage[key];
            return Promise.resolve();
        }),
    },
}));

describe("Integration: WebView Communication", () => {
    const baseProps = {
        soluCXKey: "test-key-123",
        type: "modal" as const,
        data: {
            customer_id: "user-456",
            form_id: "form-789", // Required for form mode
        },
        options: {
            height: 400,
        },
    };

    beforeEach(() => {
        jest.clearAllMocks();
        Object.keys(mockStorage).forEach(key => delete mockStorage[key]);
    });

    describe("Message Handling - Form Events", () => {
        it("should handle FORM_CLOSE message and call onClosed callback", async () => {
            const mockOnClosed = jest.fn();
            const callbacks: WidgetCallbacks = {
                onClosed: mockOnClosed,
            };

            const { getByTestId } = render(<SoluCXWidget {...baseProps} callbacks={callbacks} />);

            // Wait for widget to be ready
            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            const webview = getByTestId("webview");

            // Simulate WebView sending FORM_CLOSE message
            fireEvent(webview, "message", {
                nativeEvent: { data: "FORM_CLOSE" },
            });

            // Verify callback was called
            await waitFor(() => {
                expect(mockOnClosed).toHaveBeenCalledTimes(1);
            });
        }, 15000); // 15 second timeout for slower CI environments

        it("should handle FORM_ERROR message with error text", async () => {
            const mockOnError = jest.fn();
            const callbacks: WidgetCallbacks = {
                onError: mockOnError,
            };

            const { getByTestId } = render(<SoluCXWidget {...baseProps} callbacks={callbacks} />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            // Simulate error message with details
            fireEvent(getByTestId("webview"), "message", {
                nativeEvent: { data: "FORM_ERROR-Network timeout occurred" },
            });

            await waitFor(() => {
                expect(mockOnError).toHaveBeenCalledWith("Network timeout occurred");
            });
        });

        it("should preserve FORM_ERROR payloads containing additional hyphens", async () => {
            const mockOnError = jest.fn();
            const callbacks: WidgetCallbacks = {
                onError: mockOnError,
            };

            const { getByTestId } = render(<SoluCXWidget {...baseProps} callbacks={callbacks} />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            fireEvent(getByTestId("webview"), "message", { nativeEvent: { data: "FORM_ERROR-timeout-api-gateway" } });

            await waitFor(() => {
                expect(mockOnError).toHaveBeenCalledWith("timeout-api-gateway");
            });
        });

        it("should handle FORM_RESIZE message and update height", async () => {
            const mockOnResize = jest.fn();
            const callbacks: WidgetCallbacks = {
                onResize: mockOnResize,
            };

            const { getByTestId } = render(<SoluCXWidget {...baseProps} callbacks={callbacks} />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            // Simulate resize message
            fireEvent(getByTestId("webview"), "message", {
                nativeEvent: { data: "FORM_RESIZE-650" },
            });

            await waitFor(() => {
                expect(mockOnResize).toHaveBeenCalledWith("650");
            });
        });

        it("should handle FORM_COMPLETED message with userId", async () => {
            const mockOnCompleted = jest.fn();
            const callbacks: WidgetCallbacks = {
                onCompleted: mockOnCompleted,
            };

            const { getByTestId } = render(<SoluCXWidget {...baseProps} callbacks={callbacks} />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            // Simulate form completion
            fireEvent(getByTestId("webview"), "message", {
                nativeEvent: { data: "FORM_COMPLETED" },
            });

            await waitFor(() => {
                expect(mockOnCompleted).toHaveBeenCalledWith(expect.any(String));
            });
        });

        it("should handle FORM_PAGECHANGED message", async () => {
            const mockOnPageChanged = jest.fn();
            const callbacks: WidgetCallbacks = {
                onPageChanged: mockOnPageChanged,
            };

            const { getByTestId } = render(<SoluCXWidget {...baseProps} callbacks={callbacks} />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            // Simulate page change
            fireEvent(getByTestId("webview"), "message", {
                nativeEvent: { data: "FORM_PAGECHANGED-2" },
            });

            await waitFor(() => {
                expect(mockOnPageChanged).toHaveBeenCalledWith("2");
            });
        });

        it("should handle QUESTION_ANSWERED message", async () => {
            const mockOnQuestionAnswered = jest.fn();
            const callbacks: WidgetCallbacks = {
                onQuestionAnswered: mockOnQuestionAnswered,
            };

            const { getByTestId } = render(<SoluCXWidget {...baseProps} callbacks={callbacks} />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            // Simulate question answered
            fireEvent(getByTestId("webview"), "message", {
                nativeEvent: { data: "QUESTION_ANSWERED" },
            });

            await waitFor(() => {
                expect(mockOnQuestionAnswered).toHaveBeenCalledTimes(1);
            });
        });
    });

    describe("Message Handling - Survey Events", () => {
        const surveyProps = {
            ...baseProps,
            data: {
                customer_id: "user-456",
                // No form_id = survey mode
            },
        };

        it("should handle closeSoluCXWidget survey event", async () => {
            const mockOnClosed = jest.fn();

            const { getByTestId } = render(<SoluCXWidget {...surveyProps} callbacks={{ onClosed: mockOnClosed }} />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            // Survey uses different event names
            fireEvent(getByTestId("webview"), "message", {
                nativeEvent: { data: "closeSoluCXWidget" },
            });

            await waitFor(() => {
                expect(mockOnClosed).toHaveBeenCalledTimes(1);
            });
        });

        it("should handle resizeSoluCXWidget survey event", async () => {
            const mockOnResize = jest.fn();

            const { getByTestId } = render(<SoluCXWidget {...surveyProps} callbacks={{ onResize: mockOnResize }} />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            fireEvent(getByTestId("webview"), "message", {
                nativeEvent: { data: "resizeSoluCXWidget-800" },
            });

            await waitFor(() => {
                expect(mockOnResize).toHaveBeenCalledWith("800");
            });
        });

        it("should handle completeSoluCXWidget survey event", async () => {
            const mockOnCompleted = jest.fn();

            const { getByTestId } = render(<SoluCXWidget {...surveyProps} callbacks={{ onCompleted: mockOnCompleted }} />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            fireEvent(getByTestId("webview"), "message", {
                nativeEvent: { data: "completeSoluCXWidget" },
            });

            await waitFor(() => {
                expect(mockOnCompleted).toHaveBeenCalled();
            });
        });
    });

    describe("Edge Cases - Message Handling", () => {
        it("should handle rapid successive messages", async () => {
            const mockOnResize = jest.fn();

            const { getByTestId } = render(<SoluCXWidget {...baseProps} callbacks={{ onResize: mockOnResize }} />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            const webview = getByTestId("webview");

            // Send 10 resize messages rapidly
            for (let i = 0; i < 10; i++) {
                fireEvent(webview, "message", {
                    nativeEvent: { data: `FORM_RESIZE-${300 + i * 10}` },
                });
            }

            // All should be processed
            await waitFor(() => {
                expect(mockOnResize).toHaveBeenCalledTimes(10);
            });

            // Verify last call had correct value
            expect(mockOnResize).toHaveBeenLastCalledWith("390");
        });
    });

    describe("Close Button Integration", () => {
        it("should render close button in modal", async () => {
            const { getByText, getByTestId } = render(<SoluCXWidget {...baseProps} type="modal" />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
                expect(getByText("✕")).toBeTruthy();
            });
        });
    });

    describe("Multiple Callbacks in Single Flow", () => {
        it("should call multiple callbacks during form interaction flow", async () => {
            const mockOnPageChanged = jest.fn();
            const mockOnQuestionAnswered = jest.fn();
            const mockOnResize = jest.fn();
            const mockOnCompleted = jest.fn();
            const mockOnClosed = jest.fn();

            const callbacks: WidgetCallbacks = {
                onPageChanged: mockOnPageChanged,
                onQuestionAnswered: mockOnQuestionAnswered,
                onResize: mockOnResize,
                onCompleted: mockOnCompleted,
                onClosed: mockOnClosed,
            };

            const { getByTestId } = render(<SoluCXWidget {...baseProps} callbacks={callbacks} />);

            await waitFor(() => {
                expect(getByTestId("webview")).toBeTruthy();
            });

            const webview = getByTestId("webview");

            // Simulate complete user flow
            // 1. Form resizes
            fireEvent(webview, "message", { nativeEvent: { data: "FORM_RESIZE-400" } });

            // 2. User goes to page 2
            fireEvent(webview, "message", { nativeEvent: { data: "FORM_PAGECHANGED-2" } });

            // 3. User answers a question
            fireEvent(webview, "message", { nativeEvent: { data: "QUESTION_ANSWERED" } });

            // 4. Form resizes again
            fireEvent(webview, "message", { nativeEvent: { data: "FORM_RESIZE-500" } });

            // 5. User completes form
            fireEvent(webview, "message", { nativeEvent: { data: "FORM_COMPLETED" } });

            // 6. User closes
            fireEvent(webview, "message", { nativeEvent: { data: "FORM_CLOSE" } });

            // Verify all callbacks were called in order
            await waitFor(() => {
                expect(mockOnResize).toHaveBeenCalledTimes(2);
                expect(mockOnPageChanged).toHaveBeenCalledTimes(1);
                expect(mockOnQuestionAnswered).toHaveBeenCalledTimes(1);
                expect(mockOnCompleted).toHaveBeenCalledTimes(1);
                expect(mockOnClosed).toHaveBeenCalledTimes(1);
            });

            // Verify correct arguments
            expect(mockOnResize).toHaveBeenNthCalledWith(1, "400");
            expect(mockOnResize).toHaveBeenNthCalledWith(2, "500");
            expect(mockOnPageChanged).toHaveBeenCalledWith("2");
        });
    });
});
