'use strict';

// Mock utils methods to check they are called properly. Set up fake time now
// and fake expiry time 1 hour later.
const mockNowInSeconds = 1741604813;
const mockExpiryInSeconds = mockNowInSeconds + 3600;
const mockSetAutoRevealStatus = jest.fn().mockResolvedValue();
jest.mock( '../../../../modules/ext.checkUser.tempAccounts/ipRevealUtils.js', () => ( {
	setAutoRevealStatus: mockSetAutoRevealStatus
} ) );
jest.mock( '../../../../modules/ext.checkUser.tempAccounts/useInstrument.js' );

// Mock ipReveal methods to check they are called properly
const mockDisableAutoReveal = jest.fn().mockResolvedValue();
jest.mock( '../../../../modules/ext.checkUser.tempAccounts/ipReveal.js', () => ( {
	disableAutoReveal: mockDisableAutoReveal
} ) );

jest.mock( '../../../../modules/ext.checkUser.tempAccounts/maxDurationError.json', () => ( {
	translation: 'Duration error message'
} ), { virtual: true } );

const IPAutoRevealOffDialog = require( '../../../../modules/ext.checkUser.tempAccounts/components/IPAutoRevealOffDialog.vue' );
const useInstrument = require( '../../../../modules/ext.checkUser.tempAccounts/useInstrument.js' );
const { nextTick } = require( 'vue' );
const utils = require( '@vue/test-utils' );
const { CdxDialog } = require( '@wikimedia/codex' );

const mockSetText = jest.fn();
const renderComponent = ( props ) => {
	const defaultProps = {
		expiryTimestamp: mockExpiryInSeconds,
		toolLink: { text: mockSetText }
	};
	return utils.mount( IPAutoRevealOffDialog, {
		props: Object.assign( {}, defaultProps, props )
	} );
};

describe( 'IP auto-reveal Off dialog', () => {
	let logEvent;

	beforeEach( () => {
		logEvent = jest.fn();
		useInstrument.mockImplementation( () => logEvent );

		jest.useFakeTimers();
		jest.spyOn( Date, 'now' ).mockReturnValue( mockNowInSeconds * 1000 );
	} );

	afterEach( () => {
		jest.useRealTimers();
		jest.restoreAllMocks();
	} );

	it( 'mounts correctly', () => {
		const wrapper = renderComponent();
		expect( wrapper.exists() ).toEqual( true );
	} );

	it( 'displays the expiry time correctly', async () => {
		const wrapper = renderComponent();
		await nextTick();

		const expiryText = wrapper.find( 'p' ).html();
		expect( expiryText ).toContain( 'checkuser-ip-auto-reveal-off-dialog-text-expiry' );
		expect( expiryText ).toContain( '1:00:00' );
	} );

	it( 'updates the displayed time correctly', async () => {
		const wrapper = renderComponent();
		await nextTick();

		const initialExpiryText = wrapper.find( 'p' ).html();
		expect( initialExpiryText ).toContain( '1:00:00' );

		jest.advanceTimersByTime( 1001 );
		await nextTick();

		const updatedExpiryText = wrapper.find( 'p' ).html();
		expect( updatedExpiryText ).toContain( '0:59:59' );
		expect( logEvent ).toHaveBeenCalledTimes( 0 );
	} );

	it( 'calls setAutoRevealStatus with extended time on default action', async () => {
		const wrapper = renderComponent();
		await nextTick();

		await wrapper.findComponent( CdxDialog ).vm.$emit( 'default' );
		await nextTick();

		expect( mockSetAutoRevealStatus ).toHaveBeenCalled();
		const calledWith = mockSetAutoRevealStatus.mock.calls[ 0 ][ 0 ];

		// Check that time is extended by 600 seconds
		const expectedExpiryInSeconds = mockExpiryInSeconds + 600 - mockNowInSeconds;
		expect( calledWith ).toBeCloseTo( expectedExpiryInSeconds, 0 );
		expect( wrapper.findComponent( CdxDialog ).props( 'open' ) ).toBe( false );
		expect( logEvent ).toHaveBeenCalledTimes( 1 );
		expect( logEvent ).toHaveBeenCalledWith( 'session_extend' );
	} );

	it( 'calls setAutoRevealStatus with extended time on default action after expiry', async () => {
		const wrapper = renderComponent( { expiryTimestamp: 0 } );
		await nextTick();

		await wrapper.findComponent( CdxDialog ).vm.$emit( 'default' );
		await nextTick();

		const expectedExpiryInSeconds = 600;

		// Check that expiry is set to 600 seconds from now
		expect( mockSetAutoRevealStatus ).toHaveBeenCalledTimes( 1 );
		expect( mockSetAutoRevealStatus ).toHaveBeenLastCalledWith( expectedExpiryInSeconds );
		expect( wrapper.findComponent( CdxDialog ).props( 'open' ) ).toBe( false );
		expect( logEvent ).toHaveBeenCalledTimes( 1 );
		expect( logEvent ).toHaveBeenCalledWith( 'session_extend' );
	} );

	it( 'shows error on default action if new expiry is greater than the maximum allowed', async () => {
		require( '../../../../modules/ext.checkUser.tempAccounts/ipRevealUtils.js' ).setAutoRevealStatus.mockRejectedValue();

		// Set expiry to maximum allowed
		const expiry = mockNowInSeconds + 86400;
		const wrapper = renderComponent( { expiryTimestamp: expiry } );
		await nextTick();

		await wrapper.findComponent( CdxDialog ).vm.$emit( 'default' );
		await nextTick();

		// Check that error message is shown
		expect( mockSetAutoRevealStatus ).toHaveBeenCalledTimes( 1 );
		expect( wrapper.find( '.cdx-message' ).exists() ).toBe( true );
		expect( wrapper.findComponent( CdxDialog ).props( 'open' ) ).toBe( true );
		expect( logEvent ).toHaveBeenCalledTimes( 0 );
	} );

	it( 'calls disableAutoReveal and shows notification on off action click', async () => {
		const wrapper = renderComponent();
		await nextTick();

		await wrapper.findComponent( CdxDialog ).vm.$emit( 'primary' );
		await nextTick();

		expect( mockDisableAutoReveal ).toHaveBeenCalled();
		expect( mockSetText ).toHaveBeenCalled();
		expect( mw.notify ).toHaveBeenCalled();
		expect( wrapper.findComponent( CdxDialog ).props( 'open' ) ).toBe( false );
		expect( logEvent ).toHaveBeenCalledTimes( 1 );
		expect( logEvent ).toHaveBeenCalledWith( 'session_end' );
	} );

	it( 'correctly handles disableAutoReveal returning an error', async () => {
		mockDisableAutoReveal.mockRejectedValueOnce();

		const wrapper = renderComponent();
		await nextTick();

		await wrapper.findComponent( CdxDialog ).vm.$emit( 'primary' );
		await nextTick();

		expect( mockDisableAutoReveal ).toHaveBeenCalled();
		expect( mockSetText ).not.toHaveBeenCalled();
		expect( mw.notify ).not.toHaveBeenCalled();
		expect( wrapper.findComponent( CdxDialog ).props( 'open' ) ).toBe( true );
		expect( logEvent ).toHaveBeenCalledTimes( 0 );
	} );
} );
