/**
 * Tests for services/formatting.js
 *
 * Tests the formatting utility functions used for displaying
 * dates, durations, modes, and numbers throughout the UI.
 */

// Stub ResearchStates (used by formatStatus)
window.RESEARCH_STATUS = {
    IN_PROGRESS: 'in_progress',
    COMPLETED: 'completed',
    FAILED: 'failed',
    SUSPENDED: 'suspended',
    CANCELLED: 'cancelled',
    QUEUED: 'queued',
    PENDING: 'pending',
    ERROR: 'error',
};
window.RESEARCH_TERMINAL_STATES = new Set([
    'completed', 'suspended', 'failed', 'error', 'cancelled',
]);

// Load constants first (provides ResearchStates), then formatting
import '@js/config/constants.js';
import '@js/services/formatting.js';

const fmt = window.formatting;

describe('formatting', () => {
    describe('formatMode', () => {
        it('maps quick to Quick Summary', () => {
            expect(fmt.formatMode('quick')).toBe('Quick Summary');
        });

        it('maps detailed to Detailed Report', () => {
            expect(fmt.formatMode('detailed')).toBe('Detailed Report');
        });

        it('capitalizes unknown modes', () => {
            expect(fmt.formatMode('custom')).toBe('Custom');
        });
    });

    describe('formatDate', () => {
        it('returns Unknown for falsy input', () => {
            expect(fmt.formatDate(null)).toBe('Unknown');
            expect(fmt.formatDate('')).toBe('Unknown');
            expect(fmt.formatDate(undefined)).toBe('Unknown');
        });

        it('formats a valid ISO date', () => {
            const result = fmt.formatDate('2025-01-15T10:30:00Z');
            // Should contain month, day, and year
            expect(result).toMatch(/Jan/);
            expect(result).toMatch(/15/);
            expect(result).toMatch(/2025/);
        });

        it('appends duration in minutes and seconds', () => {
            const result = fmt.formatDate('2025-01-15T10:30:00Z', 125);
            expect(result).toContain('2m 5s');
        });

        it('appends duration in seconds only when < 60s', () => {
            const result = fmt.formatDate('2025-01-15T10:30:00Z', 45);
            expect(result).toContain('45s');
            expect(result).not.toContain('m');
        });

        it('does not append duration when null', () => {
            const result = fmt.formatDate('2025-01-15T10:30:00Z', null);
            expect(result).not.toContain('s)');
        });
    });

    describe('formatDuration', () => {
        it('returns Unknown for falsy input', () => {
            expect(fmt.formatDuration(null)).toBe('Unknown');
            expect(fmt.formatDuration(undefined)).toBe('Unknown');
            expect(fmt.formatDuration(0)).toBe('Unknown');
        });

        it('returns Unknown for NaN', () => {
            expect(fmt.formatDuration(NaN)).toBe('Unknown');
        });

        it('formats seconds only when < 60', () => {
            expect(fmt.formatDuration(45)).toBe('45s');
        });

        it('formats minutes and seconds', () => {
            expect(fmt.formatDuration(125)).toBe('2m 5s');
        });

        it('handles exact minutes', () => {
            expect(fmt.formatDuration(120)).toBe('2m 0s');
        });

        it('floors fractional seconds', () => {
            expect(fmt.formatDuration(45.7)).toBe('45s');
        });
    });

    describe('capitalizeFirstLetter', () => {
        it('capitalizes lowercase string', () => {
            expect(fmt.capitalizeFirstLetter('hello')).toBe('Hello');
        });

        it('returns empty string for falsy input', () => {
            expect(fmt.capitalizeFirstLetter('')).toBe('');
            expect(fmt.capitalizeFirstLetter(null)).toBe('');
            expect(fmt.capitalizeFirstLetter(undefined)).toBe('');
        });

        it('preserves already capitalized strings', () => {
            expect(fmt.capitalizeFirstLetter('Hello')).toBe('Hello');
        });

        it('handles single character', () => {
            expect(fmt.capitalizeFirstLetter('a')).toBe('A');
        });
    });

    describe('formatNumber', () => {
        it('returns "0" for null/undefined', () => {
            expect(fmt.formatNumber(null)).toBe('0');
            expect(fmt.formatNumber(undefined)).toBe('0');
        });

        it('returns number as-is when < 1000', () => {
            expect(fmt.formatNumber(999)).toBe('999');
        });

        it('adds comma separators for thousands', () => {
            expect(fmt.formatNumber(1000)).toBe('1,000');
            expect(fmt.formatNumber(1234567)).toBe('1,234,567');
        });

        it('handles zero', () => {
            expect(fmt.formatNumber(0)).toBe('0');
        });
    });

    describe('formatStatus', () => {
        it('delegates to ResearchStates.formatStatus', () => {
            expect(fmt.formatStatus('completed')).toBe('Completed');
            expect(fmt.formatStatus('in_progress')).toBe('In Progress');
        });
    });

    describe('formatCurrency', () => {
        it('uses 6 decimal places for tiny amounts (< $0.01)', () => {
            expect(fmt.formatCurrency(0.000123)).toBe('$0.000123');
            expect(fmt.formatCurrency(0.005)).toBe('$0.005000');
        });

        it('uses 4 decimal places for amounts < $1 (boundary: 0.01)', () => {
            expect(fmt.formatCurrency(0.01)).toBe('$0.0100');
            expect(fmt.formatCurrency(0.5)).toBe('$0.5000');
            expect(fmt.formatCurrency(0.9999)).toBe('$0.9999');
        });

        it('uses 2 decimal places for amounts >= $1', () => {
            expect(fmt.formatCurrency(1)).toBe('$1.00');
            expect(fmt.formatCurrency(12.345)).toBe('$12.35');
            expect(fmt.formatCurrency(1000)).toBe('$1000.00');
        });

        it('zero uses the tiny-amount branch', () => {
            expect(fmt.formatCurrency(0)).toBe('$0.000000');
        });
    });

    describe('generateChartColors', () => {
        it('returns empty arrays for count=0', () => {
            const r = fmt.generateChartColors(0);
            expect(r.background).toEqual([]);
            expect(r.border).toEqual([]);
        });

        it('returns `count` colors', () => {
            const r = fmt.generateChartColors(3);
            expect(r.background).toHaveLength(3);
            expect(r.border).toHaveLength(3);
        });

        it('cycles through the palette when count exceeds base length', () => {
            const r = fmt.generateChartColors(15);
            expect(r.background).toHaveLength(15);
            // First and 11th entries should be identical (palette of 10)
            expect(r.background[0]).toBe(r.background[10]);
        });

        it('border colors are opaque versions of background colors', () => {
            const r = fmt.generateChartColors(2);
            for (let i = 0; i < 2; i++) {
                // background should use alpha 0.8, border alpha 1
                expect(r.background[i]).toContain('0.8');
                expect(r.border[i]).not.toContain('0.8');
                expect(r.border[i]).toContain(', 1)');
            }
        });
    });
});
