import moment from 'moment';
import {beforeEach, describe, expect, it, vi} from 'vitest';
import {mockError, mockLoading, mockSuccess} from '@tryghost/admin-x-framework/test/hook-testing-utils';
import {renderHook} from '@testing-library/react';
import {useTopSourcesGrowth} from '@hooks/use-top-sources-growth';

// Mock external dependencies
vi.mock('@tryghost/shade/app', () => ({
    formatQueryDate: vi.fn(),
    getRangeDates: vi.fn()
}));

vi.mock('@tryghost/admin-x-framework/api/referrers', () => ({
    useTopSourcesGrowth: vi.fn()
}));

vi.mock('@src/utils/audience', () => ({
    getAudienceQueryParam: vi.fn()
}));

const mockFormatQueryDate = vi.mocked(await import('@tryghost/shade/app')).formatQueryDate;
const mockGetRangeDates = vi.mocked(await import('@tryghost/shade/app')).getRangeDates;
const mockUseTopSourcesGrowthAPI = vi.mocked(await import('@tryghost/admin-x-framework/api/referrers')).useTopSourcesGrowth;
const mockGetAudienceQueryParam = vi.mocked(await import('@src/utils/audience')).getAudienceQueryParam;

describe('useTopSourcesGrowth', () => {
    let mockStartDate: moment.Moment;
    let mockEndDate: moment.Moment;
    const mockTimezone = 'UTC';

    beforeEach(() => {
        vi.clearAllMocks();
        mockStartDate = moment('2024-01-01');
        mockEndDate = moment('2024-01-31');

        // Default mock implementations
        mockGetRangeDates.mockReturnValue({
            startDate: mockStartDate,
            endDate: mockEndDate,
            timezone: mockTimezone
        });

        mockFormatQueryDate.mockImplementation((date: moment.Moment) => date.format('YYYY-MM-DD'));

        mockGetAudienceQueryParam.mockReturnValue('all');

        mockSuccess(mockUseTopSourcesGrowthAPI, {stats: []});
    });

    it('calls useTopSourcesGrowthAPI with correct default parameters', () => {
        renderHook(() => useTopSourcesGrowth(30));

        expect(mockUseTopSourcesGrowthAPI).toHaveBeenCalledWith({
            searchParams: {
                date_from: '2024-01-01',
                date_to: '2024-01-31',
                member_status: 'all',
                order: 'signups desc',
                limit: '50',
                timezone: 'UTC'
            }
        });
    });

    it('calls useTopSourcesGrowthAPI with custom parameters', () => {
        renderHook(() => useTopSourcesGrowth(7, 'clicks desc', 25));

        expect(mockUseTopSourcesGrowthAPI).toHaveBeenCalledWith({
            searchParams: {
                date_from: '2024-01-01',
                date_to: '2024-01-31',
                member_status: 'all',
                order: 'clicks desc',
                limit: '25',
                timezone: 'UTC'
            }
        });
    });

    it('handles no timezone case', () => {
        mockGetRangeDates.mockReturnValue({
            startDate: mockStartDate,
            endDate: mockEndDate,
            // The function's actual return type is `string`, but this test exercises
            // the falsy-timezone code path in the hook. Cast to satisfy the type.
            timezone: null as unknown as string
        });

        renderHook(() => useTopSourcesGrowth(30));

        expect(mockUseTopSourcesGrowthAPI).toHaveBeenCalledWith({
            searchParams: {
                date_from: '2024-01-01',
                date_to: '2024-01-31',
                member_status: 'all',
                order: 'signups desc',
                limit: '50'
                // timezone should not be included
            }
        });
    });

    it('handles empty timezone case', () => {
        mockGetRangeDates.mockReturnValue({
            startDate: mockStartDate,
            endDate: mockEndDate,
            timezone: ''
        });

        renderHook(() => useTopSourcesGrowth(30));

        expect(mockUseTopSourcesGrowthAPI).toHaveBeenCalledWith({
            searchParams: {
                date_from: '2024-01-01',
                date_to: '2024-01-31',
                member_status: 'all',
                order: 'signups desc',
                limit: '50'
                // empty timezone should not be included
            }
        });
    });

    it('correctly formats query dates', () => {
        const customStartDate = moment('2024-06-15');
        const customEndDate = moment('2024-07-15');

        mockGetRangeDates.mockReturnValue({
            startDate: customStartDate,
            endDate: customEndDate,
            timezone: 'America/New_York'
        });

        renderHook(() => useTopSourcesGrowth(30));

        expect(mockFormatQueryDate).toHaveBeenCalledWith(customStartDate);
        expect(mockFormatQueryDate).toHaveBeenCalledWith(customEndDate);
    });

    it('returns the result from useTopSourcesGrowthAPI', () => {
        mockSuccess(mockUseTopSourcesGrowthAPI,
            {stats: [{source: 'Google', signups: 100, date: '2024-01-01', paid_conversions: 10, mrr: 1000}]}
        );

        const {result} = renderHook(() => useTopSourcesGrowth(30));

        expect(result.current.data?.stats).toHaveLength(1);
    });

    it('handles loading state', () => {
        mockLoading(mockUseTopSourcesGrowthAPI);

        const {result} = renderHook(() => useTopSourcesGrowth(30));

        expect(result.current.isLoading).toBe(true);
    });

    it('handles error state', () => {
        const apiError = new Error('API Error');
        mockError(mockUseTopSourcesGrowthAPI, apiError);

        const {result} = renderHook(() => useTopSourcesGrowth(30));

        expect(result.current.error).toBe(apiError);
    });

    it('handles various order by values', () => {
        const orderByValues = ['signups desc', 'clicks desc', 'signups asc', 'name asc'];

        orderByValues.forEach((orderBy) => {
            renderHook(() => useTopSourcesGrowth(30, orderBy));

            expect(mockUseTopSourcesGrowthAPI).toHaveBeenCalledWith({
                searchParams: expect.objectContaining({
                    order: orderBy
                })
            });
        });
    });

    it('handles various limit values', () => {
        const limitValues = [10, 25, 50, 100];

        limitValues.forEach((limit) => {
            renderHook(() => useTopSourcesGrowth(30, 'signups desc', limit));

            expect(mockUseTopSourcesGrowthAPI).toHaveBeenCalledWith({
                searchParams: expect.objectContaining({
                    limit: limit.toString()
                })
            });
        });
    });

    it('handles various range values', () => {
        const ranges = [1, 7, 30, 90];

        ranges.forEach((range) => {
            renderHook(() => useTopSourcesGrowth(range));

            expect(mockGetRangeDates).toHaveBeenCalledWith(range);
        });
    });
});
