/* @vitest-environment jsdom */

import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { AddLinkEventTracker } from '@octane/renderer';
import { act, cleanup, fireEvent, render } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { useFriends } from '../../../../hooks';
import { FriendsListView } from './FriendsListView';

vi.mock('../../../../hooks', () => ({ useFriends: vi.fn(), useMessageEvent: vi.fn(), useNotification: () => ({ simpleAlert: vi.fn() }) }));

vi.mock('./FriendsListSearchView', () => ({
    FriendsSearchView: () => <div data-testid="friend-search">search</div>
}));

vi.mock('./friends-list-group/FriendsListGroupView', () => ({
    FriendsListGroupView: () => <div data-testid="friend-group">friends</div>
}));

vi.mock('./friends-list-request/FriendsListRequestView', () => ({
    FriendsListRequestView: () => <div data-testid="friend-requests">requests</div>
}));

describe('Habbo friend-list information architecture', () => {
    const requestResponse = vi.fn();
    const requestResponseAll = vi.fn();

    beforeEach(() => {
        vi.mocked(AddLinkEventTracker).mockClear();
        requestResponse.mockClear();
        requestResponseAll.mockClear();
        vi.mocked(useFriends).mockReturnValue({
            onlineFriends: [],
            offlineFriends: [],
            requests: [{ id: 11 }, { id: 12 }],
            requestFriend: vi.fn(),
            requestResponse,
            requestResponseAll,
            settings: { categories: [] }
        } as unknown as ReturnType<typeof useFriends>);
    });

    afterEach(cleanup);

    const renderOpen = () => {
        const result = render(<FriendsListView />);
        const tracker = vi.mocked(AddLinkEventTracker).mock.calls[0][0];

        act(() => tracker.linkReceived('friends/show'));

        return { ...result, container: result.baseElement };
    };

    it('uses the original expanded friends tab structure', () => {
        const { container } = renderOpen();

        expect(container.querySelector('.hfl-panel-header')).toBeNull();
        expect(container.querySelector('.hfl-category-current')).not.toBeNull();
        expect(container.querySelector('.hfl-content [data-testid="friend-group"]')).not.toBeNull();
        expect(container.querySelector('.hfl-request-strip')).not.toBeNull();
        expect(container.querySelector('.hfl-search-strip')).not.toBeNull();
        expect(container.querySelector('[data-testid="friends-footer"]')).not.toBeNull();
    });

    it('switches the lower request and search tabs while keeping their contextual controls', () => {
        const { container } = renderOpen();

        fireEvent.click(container.querySelector<HTMLButtonElement>('.hfl-request-strip'));

        expect(container.querySelector('[data-testid="friend-group"]')).toBeNull();
        expect(container.querySelector('[data-testid="friend-requests"]')).not.toBeNull();
        expect(container.querySelector('[data-testid="requests-footer"]')).not.toBeNull();
        expect(container.querySelector('[data-testid="friends-footer"]')).toBeNull();

        fireEvent.click(container.querySelector<HTMLButtonElement>('[data-action="accept-all"]'));
        fireEvent.click(container.querySelector<HTMLButtonElement>('[data-action="dismiss-all"]'));

        // One packet for all of them, not one per request: AcceptFriendRequestEvent is
        // rate limited to one every 500 ms and drops the rest in silence.
        expect(requestResponseAll).toHaveBeenNthCalledWith(1, true);
        expect(requestResponseAll).toHaveBeenNthCalledWith(2, false);
        expect(requestResponse).not.toHaveBeenCalled();

        fireEvent.click(container.querySelector<HTMLButtonElement>('.hfl-search-strip'));

        expect(container.querySelector('[data-testid="friend-requests"]')).toBeNull();
        expect(container.querySelector('[data-testid="friend-search"]')).not.toBeNull();
        expect(container.querySelector('[data-testid="requests-footer"]')).toBeNull();
    });

    it('does not reserve a request tab when there are no pending requests', () => {
        vi.mocked(useFriends).mockReturnValue({
            onlineFriends: [],
            offlineFriends: [],
            requests: [],
            requestFriend: vi.fn(),
            requestResponse,
            requestResponseAll,
            settings: { categories: [] }
        } as unknown as ReturnType<typeof useFriends>);

        const { container } = renderOpen();

        expect(container.querySelector('.hfl-request-strip')).toBeNull();
        expect(container.querySelector('.hfl-search-strip')).not.toBeNull();
    });

    it('collapses and reopens the selected friends tab', () => {
        const { container } = renderOpen();
        const friendsTab = container.querySelector<HTMLButtonElement>('.hfl-category-current');

        expect(friendsTab).toHaveAttribute('aria-expanded', 'true');

        fireEvent.click(friendsTab);

        expect(container.querySelector('.habbo-friend-list')).toHaveClass('collapsed-mode');
        expect(friendsTab).toHaveAttribute('aria-expanded', 'false');
        expect(container.querySelector('.hfl-content')).toBeNull();
        expect(container.querySelector('[data-testid="friends-footer"]')).toBeNull();
        expect(container.querySelector('.hfl-search-strip')).not.toBeNull();

        fireEvent.click(friendsTab);

        expect(container.querySelector('.habbo-friend-list')).not.toHaveClass('collapsed-mode');
        expect(friendsTab).toHaveAttribute('aria-expanded', 'true');
        expect(container.querySelector('.hfl-content')).not.toBeNull();
        expect(container.querySelector('[data-testid="friends-footer"]')).not.toBeNull();
    });

    it('shows AIR counts on the tab headers', () => {
        vi.mocked(useFriends).mockReturnValue({
            onlineFriends: [{ id: 1, name: 'a', online: true }],
            offlineFriends: [],
            requests: [{ id: 11 }, { id: 12 }],
            requestFriend: vi.fn(),
            requestResponse,
            requestResponseAll,
            settings: { categories: [] }
        } as unknown as ReturnType<typeof useFriends>);

        const { container } = renderOpen();

        expect(container.querySelector('.hfl-category-current')).toHaveTextContent(/\(1\)$/);
        expect(container.querySelector('.hfl-request-strip')).toHaveTextContent(/\(2\)$/);
        expect(container.querySelector('.hfl-search-strip')).toHaveTextContent(/\(0\)$/);
    });

    it('keeps invite and home off until a friend is selected, as AIR does', () => {
        const { container } = renderOpen();

        expect(container.querySelector('.hfl-footer-button.invite')).toBeDisabled();
        expect(container.querySelector('.hfl-footer-button.home')).toBeDisabled();
        expect(container.querySelector('.hfl-footer-button.delete')).toBeDisabled();
    });

    it('refuses to accept every request when the friend list would be over its limit', () => {
        const showFriendListFullAlert = vi.fn();

        vi.mocked(useFriends).mockReturnValue({
            onlineFriends: [],
            offlineFriends: [],
            requests: [{ id: 11 }, { id: 12 }],
            requestFriend: vi.fn(),
            requestResponse,
            requestResponseAll,
            isFriendListFull: (adding: number) => adding > 1,
            showFriendListFullAlert,
            settings: { categories: [] }
        } as unknown as ReturnType<typeof useFriends>);

        const { container } = renderOpen();

        fireEvent.click(container.querySelector<HTMLButtonElement>('.hfl-request-strip'));
        fireEvent.click(container.querySelector<HTMLButtonElement>('[data-action="accept-all"]'));

        expect(showFriendListFullAlert).toHaveBeenCalledOnce();
        expect(requestResponseAll).not.toHaveBeenCalled();
    });

    it('uses the original fixed geometry instead of stretching the window', () => {
        const css = readFileSync(join(process.cwd(), 'src/components/friends/views/friends-list/FriendsListView.css'), 'utf8');

        expect(css).toMatch(/\.habbo-friend-list\s*\{[\s\S]*?width:\s*230px;/);
        expect(css).toMatch(/grid-template-rows:\s*24px 17px minmax\(0, 1fr\) 41px 17px 33px;/);
        expect(css).toMatch(/\.hfl-footer-border\s*\{[\s\S]*?width:\s*calc\(100% - 10px\);/);
        expect(css).toMatch(/\.habbo-friend-list\.collapsed-mode\s*\{[^}]*height:\s*auto;/s);
    });
});
