// SPDX-License-Identifier: AGPL-3.0-or-later

import {FriendSourceFlags, RelationshipTypes} from '@fluxer/constants/src/UserConstants';
import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest';
import {createTestAccount} from '../../auth/tests/AuthTestUtils';
import {type ApiTestHarness, createApiTestHarness} from '../../test/ApiTestHarness';
import {HTTP_STATUS} from '../../test/TestConstants';
import {createBuilder} from '../../test/TestRequestBuilder';
import {
	assertRelationshipId,
	assertRelationshipType,
	createFriendship,
	sendFriendRequest,
} from './RelationshipTestUtils';

describe('FriendRequestSourceFlags', () => {
	let harness: ApiTestHarness;
	beforeAll(async () => {
		harness = await createApiTestHarness();
	});
	beforeEach(async () => {
		await harness.reset();
	});
	afterAll(async () => {
		await harness?.shutdown();
	});
	describe('NO_RELATION allows friend requests from strangers', () => {
		test('user with NO_RELATION flag can receive friend requests from users with no mutual friends or guilds', async () => {
			const alice = await createTestAccount(harness);
			const bob = await createTestAccount(harness);
			await createBuilder(harness, bob.token)
				.patch('/users/@me/settings')
				.body({friend_source_flags: FriendSourceFlags.NO_RELATION})
				.expect(HTTP_STATUS.OK)
				.execute();
			const {json: outgoing} = await sendFriendRequest(harness, alice.token, bob.userId);
			assertRelationshipId(outgoing, bob.userId);
			assertRelationshipType(outgoing, RelationshipTypes.OUTGOING_REQUEST);
		});
		test('NO_RELATION with all flags set allows friend requests from strangers', async () => {
			const alice = await createTestAccount(harness);
			const bob = await createTestAccount(harness);
			await createBuilder(harness, bob.token)
				.patch('/users/@me/settings')
				.body({
					friend_source_flags:
						FriendSourceFlags.NO_RELATION | FriendSourceFlags.MUTUAL_FRIENDS | FriendSourceFlags.MUTUAL_GUILDS,
				})
				.expect(HTTP_STATUS.OK)
				.execute();
			const {json: outgoing} = await sendFriendRequest(harness, alice.token, bob.userId);
			assertRelationshipId(outgoing, bob.userId);
			assertRelationshipType(outgoing, RelationshipTypes.OUTGOING_REQUEST);
		});
	});
	describe('without NO_RELATION blocks strangers', () => {
		test('user with only MUTUAL_FRIENDS rejects requests from strangers with no mutual friends', async () => {
			const alice = await createTestAccount(harness);
			const bob = await createTestAccount(harness);
			await createBuilder(harness, bob.token)
				.patch('/users/@me/settings')
				.body({friend_source_flags: FriendSourceFlags.MUTUAL_FRIENDS})
				.expect(HTTP_STATUS.OK)
				.execute();
			await createBuilder(harness, alice.token)
				.post(`/users/@me/relationships/${bob.userId}`)
				.expect(HTTP_STATUS.BAD_REQUEST, 'FRIEND_REQUEST_BLOCKED')
				.execute();
		});
		test('user with only MUTUAL_GUILDS rejects requests from strangers with no mutual guilds', async () => {
			const alice = await createTestAccount(harness);
			const bob = await createTestAccount(harness);
			await createBuilder(harness, bob.token)
				.patch('/users/@me/settings')
				.body({friend_source_flags: FriendSourceFlags.MUTUAL_GUILDS})
				.expect(HTTP_STATUS.OK)
				.execute();
			await createBuilder(harness, alice.token)
				.post(`/users/@me/relationships/${bob.userId}`)
				.expect(HTTP_STATUS.BAD_REQUEST, 'FRIEND_REQUEST_BLOCKED')
				.execute();
		});
		test('user with zero flags rejects all friend requests', async () => {
			const alice = await createTestAccount(harness);
			const bob = await createTestAccount(harness);
			await createBuilder(harness, bob.token)
				.patch('/users/@me/settings')
				.body({friend_source_flags: 0})
				.expect(HTTP_STATUS.OK)
				.execute();
			await createBuilder(harness, alice.token)
				.post(`/users/@me/relationships/${bob.userId}`)
				.expect(HTTP_STATUS.BAD_REQUEST, 'FRIEND_REQUEST_BLOCKED')
				.execute();
		});
	});
	describe('MUTUAL_FRIENDS allows requests from users sharing mutual friends', () => {
		test('user with MUTUAL_FRIENDS flag accepts request when they share a mutual friend', async () => {
			const alice = await createTestAccount(harness);
			const bob = await createTestAccount(harness);
			const charlie = await createTestAccount(harness);
			await createFriendship(harness, alice, charlie);
			await createFriendship(harness, bob, charlie);
			await createBuilder(harness, bob.token)
				.patch('/users/@me/settings')
				.body({friend_source_flags: FriendSourceFlags.MUTUAL_FRIENDS})
				.expect(HTTP_STATUS.OK)
				.execute();
			const {json: outgoing} = await sendFriendRequest(harness, alice.token, bob.userId);
			assertRelationshipId(outgoing, bob.userId);
			assertRelationshipType(outgoing, RelationshipTypes.OUTGOING_REQUEST);
		});
	});
	describe('default flags', () => {
		test('adult users default to allowing everyone (NO_RELATION set)', async () => {
			const alice = await createTestAccount(harness);
			const bob = await createTestAccount(harness);
			const {json: outgoing} = await sendFriendRequest(harness, alice.token, bob.userId);
			assertRelationshipId(outgoing, bob.userId);
			assertRelationshipType(outgoing, RelationshipTypes.OUTGOING_REQUEST);
		});
		test('teen users default to blocking strangers', async () => {
			const alice = await createTestAccount(harness);
			const bob = await createTestAccount(harness, {dateOfBirth: '2012-01-01'});
			await createBuilder(harness, alice.token)
				.post(`/users/@me/relationships/${bob.userId}`)
				.expect(HTTP_STATUS.BAD_REQUEST, 'FRIEND_REQUEST_BLOCKED')
				.execute();
		});
		test('teen users can receive requests from mutual friends', async () => {
			const alice = await createTestAccount(harness);
			const bob = await createTestAccount(harness, {dateOfBirth: '2012-01-01'});
			const charlie = await createTestAccount(harness);
			await createFriendship(harness, alice, charlie);
			await createFriendship(harness, bob, charlie);
			const {json: outgoing} = await sendFriendRequest(harness, alice.token, bob.userId);
			assertRelationshipId(outgoing, bob.userId);
			assertRelationshipType(outgoing, RelationshipTypes.OUTGOING_REQUEST);
		});
	});
	describe('settings persistence', () => {
		test('NO_RELATION flag is preserved after settings update', async () => {
			const bob = await createTestAccount(harness);
			await createBuilder(harness, bob.token)
				.patch('/users/@me/settings')
				.body({friend_source_flags: FriendSourceFlags.NO_RELATION})
				.expect(HTTP_STATUS.OK)
				.execute();
			const {json: settings} = await createBuilder<{
				friend_source_flags: number;
			}>(harness, bob.token)
				.get('/users/@me/settings')
				.executeWithResponse();
			expect(settings.friend_source_flags & FriendSourceFlags.NO_RELATION).toBe(FriendSourceFlags.NO_RELATION);
		});
	});
});
