// #region SettingsPageUxTest:Module [TYPE Function]
// @SEMANTICS: settings, page, ux-tests, save-flow
// @RELATION DEPENDS_ON -> [SettingsPage]
// @PURPOSE: Test UX states and transitions
// @LAYER UI (Tests)


import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
import SettingsPage from '../+page.svelte';
import { api } from '$lib/api';
import { addToast } from '$lib/toasts.svelte.js';

vi.mock('$lib/api', () => ({
    api: {
        getConsolidatedSettings: vi.fn(),
        requestApi: vi.fn(),
        putApi: vi.fn(),
        postApi: vi.fn(),
        updateConsolidatedSettings: vi.fn()
    }
}));

vi.mock('$lib/toasts.svelte.js', () => ({
    addToast: vi.fn()
}));

vi.mock('$lib/i18n/index.svelte.js', () => ({
    t: {
        subscribe: (fn) => {
            fn({
                settings: {
                    title: 'Settings',
                    migration_sync: 'Migration Sync',
                    migration_sync_title: 'Cross-Environment ID Synchronization',
                    migration_sync_description: 'Sync IDs across environments',
                    sync_schedule: 'Sync Schedule',
                    sync_now: 'Sync Now',
                    syncing: 'Syncing...',
                    saving: 'Saving...',
                    environments: 'Environments',
                    logging: 'Logging',
                    logging_description: 'Configure logging',
                    log_level: 'Log Level',
                    task_log_level: 'Task Log Level',
                    enable_belief_state: 'Enable Belief State',
                    llm: 'LLM',
                    storage: 'Storage',
                    save_success: 'Settings saved',
                    save_failed: 'Failed',
                    save_logging: 'Save Logging Config',
                    env_description: 'Configure environments',
                    load_failed: 'Failed to load settings',
                    migration_sync_failed: 'Sync failed'
                },
                common: { refresh: 'Refresh', retry: 'Retry', save: 'Save' },
                tasks: { cron_label: 'Cron Expression', cron_hint: 'Standard cron format' },
                nav: { settings_git: 'Git' },
            });
            return () => { };
        }
    },
    _: vi.fn((key) => key)
}));

// Mock child components
vi.mock('../../components/llm/ProviderConfig.svelte', () => ({
    default: class ProviderConfigMock {
        constructor(options) {
            options.target.innerHTML = `<div data-testid="provider-config"></div>`;
        }
    }
}));

describe('SettingsPage UX Contracts', () => {
    const mockSettings = {
        environments: [],
        logging: { level: 'INFO', task_log_level: 'INFO', enable_belief_state: false },
        llm: {}
    };

    const mockMigrationSettings = {
        cron: "0 2 * * *"
    };

    beforeEach(() => {
        vi.clearAllMocks();
    });

    // @UX_STATE: Loading -> Shows skeleton loader
    // @UX_STATE: Loaded -> Shows tabbed settings interface
    it('should transition from Loading to Loaded state', async () => {
        // Delay resolution to capture loading state
        let resolveSettings;
        api.getConsolidatedSettings.mockImplementation(() => new Promise(resolve => {
            resolveSettings = resolve;
        }));

        api.requestApi.mockImplementation((url) => {
            if (url === '/migration/settings') return Promise.resolve(mockMigrationSettings);
            if (url.includes('/migration/mappings-data')) return Promise.resolve({ items: [], total: 0 });
            return Promise.resolve({});
        });

        render(SettingsPage);

        // Assert Loading skeleton is present (by checking for the pulse class)
        // Note: checking for classes used in skeleton
        const skeletonElements = document.querySelectorAll('.animate-pulse');
        expect(skeletonElements.length).toBeGreaterThan(0);

        // Resolve the API call
        resolveSettings(mockSettings);

        // Assert Loaded state - use getAllByText because 'Environments' may appear
        // both as tab text and section heading on the default tab
        await waitFor(() => {
            expect(screen.getByText('Settings')).toBeTruthy();
            expect(screen.getAllByText('Environments').length).toBeGreaterThan(0);
        });
    });

    // @UX_STATE: Error -> Shows error banner with retry button
    it('should show error banner when loading fails', async () => {
        api.getConsolidatedSettings.mockRejectedValue(new Error('Network Error'));
        api.requestApi.mockResolvedValue(mockMigrationSettings);

        render(SettingsPage);

        await waitFor(() => {
            expect(screen.getByText('Network Error')).toBeTruthy();
            expect(screen.getByText('Retry')).toBeTruthy();
        });
    });

    // @UX_RECOVERY: Refresh button reloads settings data
    it('should reload settings data when retry button is clicked', async () => {
        let callCount = 0;
        api.getConsolidatedSettings.mockImplementation(async () => {
            callCount++;
            if (callCount === 1) throw new Error('First call failed');
            return mockSettings;
        });

        api.requestApi.mockImplementation((url) => {
            if (url === '/migration/settings') return Promise.resolve(mockMigrationSettings);
            if (url.includes('/migration/mappings-data')) return Promise.resolve({ items: [], total: 0 });
            return Promise.resolve({});
        });

        render(SettingsPage);

        // Wait for error state
        await waitFor(() => {
            expect(screen.getByText('First call failed')).toBeTruthy();
        });

        // Click retry
        const retryBtn = screen.getByText('Retry');
        await fireEvent.click(retryBtn);

        // Verify recovery (Loaded state) - use getAllByText because 'Environments'
        // appears both as tab text and section heading
        await waitFor(() => {
            expect(screen.queryByText('First call failed')).toBeNull();
            expect(screen.getAllByText('Environments').length).toBeGreaterThan(0);
        });
        // We expect it to have been called twice (1. initial mount, 2. retry click)
        expect(api.getConsolidatedSettings).toHaveBeenCalledTimes(2);
    });

    // @UX_FEEDBACK: Toast notifications on save success/failure
    it('should show success toast when settings are saved', async () => {
        api.getConsolidatedSettings.mockResolvedValue(mockSettings);
        api.requestApi.mockResolvedValue(mockMigrationSettings);
        api.updateConsolidatedSettings.mockResolvedValue({});

        render(SettingsPage);
        await waitFor(() => expect(screen.getByText('Settings')).toBeTruthy());

        // Navigate to Logging tab where the Save button is
        // 'Logging' appears in both the tab and section heading; use getAllByText
        await fireEvent.click(screen.getAllByText('Logging')[0]);

        const saveBtn = screen.getByText('Save Logging Config');
        await fireEvent.click(saveBtn);

        await waitFor(() => {
            expect(addToast).toHaveBeenCalledWith('Settings saved', 'success');
        });
    });

    it('should show error toast when settings save fails', async () => {
        api.getConsolidatedSettings.mockResolvedValue(mockSettings);
        api.requestApi.mockResolvedValue(mockMigrationSettings);
        api.updateConsolidatedSettings.mockRejectedValue(new Error('Save Error'));

        render(SettingsPage);
        await waitFor(() => expect(screen.getByText('Settings')).toBeTruthy());

        // Navigate to Logging tab where the Save button is
        // 'Logging' appears in both the tab and section heading; use getAllByText
        await fireEvent.click(screen.getAllByText('Logging')[0]);

        const saveBtn = screen.getByText('Save Logging Config');
        await fireEvent.click(saveBtn);

        await waitFor(() => {
            expect(addToast).toHaveBeenCalledWith('Failed', 'error');
        });
    });
});

// #endregion SettingsPageUxTest:Module
