// [DEF:frontend.src.components.__tests__.task_log_viewer:Module]
// @TIER: STANDARD
// @SEMANTICS: tests, task-log, viewer, mount, components
// @PURPOSE: Unit tests for TaskLogViewer component by mounting it and observing the DOM.
// @LAYER: UI (Tests)
// @RELATION: VERIFIES -> frontend/src/components/TaskLogViewer.svelte
// @INVARIANT: Duplicate logs are never appended. Polling only active for in-progress tasks.
// @TEST_CONTRACT: TaskLogViewerPropsAndLogStream -> RenderedLogTimeline
// @TEST_SCENARIO: historical_and_realtime_merge -> Historical logs render and realtime logs append without duplication.
// @TEST_FIXTURE: valid_viewer -> INLINE_JSON
// @TEST_EDGE: no_task_id -> Null taskId does not trigger fetch.
// @TEST_EDGE: fetch_failure -> Network failure renders recoverable error state with retry action.
// @TEST_EDGE: duplicate_realtime_entry -> Existing log is not duplicated when repeated in realtime stream.
// @TEST_INVARIANT: no_duplicate_log_rows -> VERIFIED_BY: [historical_and_realtime_merge, duplicate_realtime_entry]

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/svelte';
import TaskLogViewer from '../TaskLogViewer.svelte';
import { getTaskLogs } from '../../services/taskService.js';

vi.mock('../../services/taskService.js', () => ({
    getTaskLogs: vi.fn()
}));

const getTaskLogsMock = vi.mocked(getTaskLogs);

vi.mock('../../lib/i18n', () => ({
    t: {
        subscribe: (fn) => {
            fn({
                tasks: {
                    loading: 'Loading...',
                    logs_title: 'Task Logs'
                },
                common: {
                    retry: 'Retry',
                    close: 'Close'
                }
            });
            return () => { };
        }
    }
}));

describe('TaskLogViewer Component', () => {
    beforeEach(() => {
        vi.clearAllMocks();
    });

    it('renders loading state initially', () => {
        getTaskLogsMock.mockResolvedValue([]);
        render(TaskLogViewer, { inline: true, taskId: 'task-123' });
        expect(screen.getByText('Loading...')).toBeDefined();
    });

    it('fetches and displays historical logs', async () => {
        getTaskLogsMock.mockResolvedValue([
            { timestamp: '2024-01-01T00:00:00', level: 'INFO', message: 'Historical log entry' }
        ]);

        render(TaskLogViewer, { inline: true, taskId: 'task-123' });

        await waitFor(() => {
            expect(screen.getByText(/Historical log entry/)).toBeDefined();
        });

        expect(getTaskLogs).toHaveBeenCalledWith('task-123');
    });

    it('displays error message on fetch failure', async () => {
        getTaskLogsMock.mockRejectedValue(new Error('Network error fetching logs'));

        render(TaskLogViewer, { inline: true, taskId: 'task-123' });

        await waitFor(() => {
            expect(screen.getByText('Network error fetching logs')).toBeDefined();
            expect(screen.getByText('Retry')).toBeDefined();
        });
    });

    it('appends real-time logs passed as props', async () => {
        getTaskLogsMock.mockResolvedValue([
            { timestamp: '2024-01-01T00:00:00', level: 'INFO', message: 'Historical log entry' }
        ]);

        const { rerender } = render(TaskLogViewer, {
            inline: true,
            taskId: 'task-123',
            realTimeLogs: []
        });

        await waitFor(() => {
            expect(screen.getByText(/Historical log entry/)).toBeDefined();
        });

        // Simulate receiving a new real-time log
        await rerender({
            inline: true,
            taskId: 'task-123',
            realTimeLogs: [
                { timestamp: '2024-01-01T00:00:01', level: 'DEBUG', message: 'Realtime log entry' }
            ]
        });

        await waitFor(() => {
            expect(screen.getByText(/Realtime log entry/)).toBeDefined();
        });
    });

    it('deduplicates real-time logs that are already in historical logs', async () => {
        getTaskLogsMock.mockResolvedValue([
            { timestamp: '2024-01-01T00:00:00', level: 'INFO', message: 'Duplicate log entry' }
        ]);

        const { rerender } = render(TaskLogViewer, {
            inline: true,
            taskId: 'task-123',
            realTimeLogs: []
        });

        await waitFor(() => {
            expect(screen.getByText(/Duplicate log entry/)).toBeDefined();
        });

        // Pass the exact same log as realtime
        await rerender({
            inline: true,
            taskId: 'task-123',
            realTimeLogs: [
                { timestamp: '2024-01-01T00:00:00', level: 'INFO', message: 'Duplicate log entry' }
            ]
        });

        // Wait a bit to ensure no explosive re-renders or double additions
        await new Promise((r) => setTimeout(r, 50));

        // In RTL, if there were duplicates, getAllByText would return > 1 elements.
        // getByText asserts there is exactly *one* match.
        expect(() => screen.getByText(/Duplicate log entry/)).not.toThrow();
    });

    // @TEST_FIXTURE valid_viewer
    it('fetches and displays historical logs in modal mode under valid_viewer fixture', async () => {
        getTaskLogsMock.mockResolvedValue([
            { timestamp: '2024-01-01T00:00:00', level: 'INFO', message: 'Modal log entry' }
        ]);

        render(TaskLogViewer, { show: true, inline: false, taskId: 'task-123' });

        await waitFor(() => {
            expect(screen.getByText(/Modal log entry/)).toBeDefined();
            expect(screen.getByText('Task Logs')).toBeDefined();
        });

        expect(getTaskLogs).toHaveBeenCalledWith('task-123');
    });

    // @TEST_EDGE no_task_id
    it('does not fetch logs if taskId is null', () => {
        render(TaskLogViewer, { inline: true, taskId: null });
        expect(getTaskLogs).not.toHaveBeenCalled();
    });

    // @UX_FEEDBACK
    it('passes autoScroll feedback properly down to the panel by rendering without crashing', () => {
        const { component } = render(TaskLogViewer, { inline: true, taskId: 'task-123' });
        expect(component).toBeDefined();
    });
});
// [/DEF:frontend.src.components.__tests__.task_log_viewer:Module]
