/**
 * @jest-environment jsdom
 */

/*
 * Runtime Web – Unified Serverless Framework for Full-Stack TypeScript Applications
 * Documentation: https://runtimeweb.com/docs/getting-started
 *
 * @template: ai-chat
 */

/**
 * Property-based tests for SessionIndicator component.
 *
 * **Property 8: Session Indicator Behavior**
 * **Validates: Requirements 9.1, 9.5**
 *
 * Property: For any valid sessionId, the SessionIndicator component SHALL render
 * showing the message count. For any SessionIndicator with onNewSession callback,
 * a "new conversation" option SHALL be available.
 *
 * @module components/chat/__tests__/SessionIndicator.property.test
 */

import * as React from 'react';
import * as fc from 'fast-check';
import { render, cleanup, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { SessionIndicator } from '../SessionIndicator';

afterEach(() => {
    cleanup();
    jest.clearAllMocks();
});

/**
 * Arbitrary generator for valid session IDs.
 */
const sessionIdArbitrary = (): fc.Arbitrary<string> => {
    return fc.uuid();
};

/**
 * Arbitrary generator for message counts.
 */
const messageCountArbitrary = (): fc.Arbitrary<number> => {
    return fc.integer({ min: 0, max: 1000 });
};

describe('SessionIndicator Property Tests', () => {
    /**
     * **Property 8: Session Indicator Behavior**
     * **Validates: Requirements 9.1, 9.5**
     */
    describe('Property 8: Session Indicator Behavior', () => {
        /**
         * Validates: Requirement 9.1
         * For any valid sessionId, the SessionIndicator SHALL render.
         */
        it('renders for any valid sessionId', () => {
            fc.assert(
                fc.property(sessionIdArbitrary(), messageCountArbitrary(), (sessionId, messageCount) => {
                    const { unmount } = render(
                        <SessionIndicator sessionId={sessionId} messageCount={messageCount} />
                    );

                    const indicator = screen.getByTestId('session-indicator');
                    expect(indicator).toBeInTheDocument();

                    unmount();
                }),
                { numRuns: 100 },
            );
        });

        /**
         * Validates: Requirement 9.1
         * The SessionIndicator SHALL display the message count.
         */
        it('displays message count', () => {
            fc.assert(
                fc.property(
                    sessionIdArbitrary(),
                    fc.integer({ min: 1, max: 1000 }),
                    (sessionId, messageCount) => {
                        const { unmount } = render(
                            <SessionIndicator sessionId={sessionId} messageCount={messageCount} />
                        );

                        const text = screen.getByTestId('session-indicator-text');
                        expect(text.textContent).toContain(messageCount.toString());
                        expect(text.textContent).toContain('messages');

                        unmount();
                    }
                ),
                { numRuns: 100 },
            );
        });

        /**
         * Validates: Requirement 9.1
         * For zero messages, SHALL display "New chat".
         */
        it('displays "New chat" for zero messages', () => {
            fc.assert(
                fc.property(sessionIdArbitrary(), (sessionId) => {
                    const { unmount } = render(
                        <SessionIndicator sessionId={sessionId} messageCount={0} />
                    );

                    const text = screen.getByTestId('session-indicator-text');
                    expect(text.textContent).toBe('New chat');

                    unmount();
                }),
                { numRuns: 50 },
            );
        });

        /**
         * Validates: Requirement 9.5
         * With onNewSession callback, "new conversation" option SHALL be available.
         */
        it('shows new conversation option when onNewSession is provided', () => {
            fc.assert(
                fc.property(sessionIdArbitrary(), messageCountArbitrary(), (sessionId, messageCount) => {
                    const onNewSession = jest.fn();
                    const { unmount } = render(
                        <SessionIndicator
                            sessionId={sessionId}
                            messageCount={messageCount}
                            onNewSession={onNewSession}
                        />
                    );

                    // Click to open menu
                    const button = screen.getByTestId('session-indicator-button');
                    fireEvent.click(button);

                    // Menu should be visible
                    const menu = screen.getByTestId('session-indicator-menu');
                    expect(menu).toBeInTheDocument();

                    // New session option should be available
                    const newSessionButton = screen.getByTestId('session-indicator-new-session');
                    expect(newSessionButton).toBeInTheDocument();

                    unmount();
                }),
                { numRuns: 50 },
            );
        });

        /**
         * Validates: Requirement 9.5
         * Clicking new conversation option SHALL invoke onNewSession.
         */
        it('clicking new conversation invokes onNewSession', () => {
            fc.assert(
                fc.property(sessionIdArbitrary(), messageCountArbitrary(), (sessionId, messageCount) => {
                    const onNewSession = jest.fn();
                    const { unmount } = render(
                        <SessionIndicator
                            sessionId={sessionId}
                            messageCount={messageCount}
                            onNewSession={onNewSession}
                        />
                    );

                    // Click to open menu
                    const button = screen.getByTestId('session-indicator-button');
                    fireEvent.click(button);

                    // Click new session option
                    const newSessionButton = screen.getByTestId('session-indicator-new-session');
                    fireEvent.click(newSessionButton);

                    expect(onNewSession).toHaveBeenCalledTimes(1);

                    unmount();
                }),
                { numRuns: 50 },
            );
        });

        /**
         * Validates: Empty sessionId returns null.
         */
        it('returns null for empty sessionId', () => {
            fc.assert(
                fc.property(messageCountArbitrary(), (messageCount) => {
                    const { container, unmount } = render(
                        <SessionIndicator sessionId="" messageCount={messageCount} />
                    );

                    expect(container.querySelector('.session-indicator')).toBeNull();

                    unmount();
                }),
                { numRuns: 20 },
            );
        });

        /**
         * Validates: Accessibility - button has aria-expanded and aria-haspopup.
         */
        it('button has proper ARIA attributes', () => {
            fc.assert(
                fc.property(sessionIdArbitrary(), messageCountArbitrary(), (sessionId, messageCount) => {
                    const onNewSession = jest.fn();
                    const { unmount } = render(
                        <SessionIndicator
                            sessionId={sessionId}
                            messageCount={messageCount}
                            onNewSession={onNewSession}
                        />
                    );

                    const button = screen.getByTestId('session-indicator-button');
                    expect(button).toHaveAttribute('aria-haspopup', 'true');
                    expect(button).toHaveAttribute('aria-expanded', 'false');

                    // After click, aria-expanded should be true
                    fireEvent.click(button);
                    expect(button).toHaveAttribute('aria-expanded', 'true');

                    unmount();
                }),
                { numRuns: 50 },
            );
        });

        /**
         * Validates: Menu closes after selecting new session.
         */
        it('menu closes after selecting new session', () => {
            fc.assert(
                fc.property(sessionIdArbitrary(), messageCountArbitrary(), (sessionId, messageCount) => {
                    const onNewSession = jest.fn();
                    const { unmount } = render(
                        <SessionIndicator
                            sessionId={sessionId}
                            messageCount={messageCount}
                            onNewSession={onNewSession}
                        />
                    );

                    // Open menu
                    const button = screen.getByTestId('session-indicator-button');
                    fireEvent.click(button);

                    // Click new session
                    const newSessionButton = screen.getByTestId('session-indicator-new-session');
                    fireEvent.click(newSessionButton);

                    // Menu should be closed
                    expect(screen.queryByTestId('session-indicator-menu')).toBeNull();

                    unmount();
                }),
                { numRuns: 50 },
            );
        });
    });
});
