/**
 * SV-series: Section visibility / feature_flags (Fase 12.2).
 *
 *   SV-01 default ON — fresh user + fresh collective, every known section
 *         resolves to true.
 *   SV-02 user OFF + collective absent → section OFF for that user, ON for
 *         everyone else.
 *   SV-03 collective OFF beats user ON (admin opt-out applies to everyone,
 *         including users who tried to opt back in).
 *   SV-04 a user can update their own `feature_flags` (RLS write-path
 *         piggybacks on users_update_own).
 *   SV-05 an admin can update the collective `feature_flags`; a non-admin
 *         member cannot (RLS write-path piggybacks on collectives_update).
 *   SV-06 the same user in two collectives sees independent collective
 *         overrides even though the user-level flag is shared (foundational
 *         check for the SV-03 e2e spec).
 */
import { describe, it, expect, afterAll } from 'vitest';
import {
	createClientAs,
	createAdminClient
} from '../src/supabase-clients.js';
import {
	ANA_ID,
	BORJA_ID,
	CARMEN_ID,
	COLLECTIVE_ID
} from '../src/seed-constants.js';

const admin = createAdminClient();
const createdCollectiveIds: string[] = [];

// Reset both seed rows to a clean `{}` state at the top of every test so the
// previous one doesn't leak. The user/collective rows themselves are seeded
// (and never deleted) — only the JSONB column is reset.
async function resetFlags() {
	await admin
		.from('users')
		.update({ feature_flags: {} })
		.in('id', [ANA_ID, BORJA_ID, CARMEN_ID]);
	await admin
		.from('collectives')
		.update({ feature_flags: {} })
		.eq('id', COLLECTIVE_ID);
}

afterAll(async () => {
	await resetFlags();
	if (createdCollectiveIds.length) {
		await admin.from('collectives').delete().in('id', createdCollectiveIds);
	}
});

describe('section_enabled() precedence (integration)', () => {
	it('SV-01: default ON — empty flags on both layers, every known section is enabled', async () => {
		await resetFlags();
		const borja = await createClientAs(BORJA_ID);

		const sections = ['lists', 'tasks', 'notes', 'search'];
		for (const section of sections) {
			const { data, error } = await borja.rpc('section_enabled', {
				p_section: section,
				p_user: BORJA_ID,
				p_collective: COLLECTIVE_ID
			});
			expect(error).toBeNull();
			expect(data).toBe(true);
		}
	});

	it('SV-02: user OFF only — applies to that user, not to a peer in the same collective', async () => {
		await resetFlags();

		// Borja opts out of tasks for himself.
		const borja = await createClientAs(BORJA_ID);
		const upd = await borja
			.from('users')
			.update({ feature_flags: { tasks: false } })
			.eq('id', BORJA_ID);
		expect(upd.error).toBeNull();

		// Borja sees tasks OFF.
		const borjaEval = await borja.rpc('section_enabled', {
			p_section: 'tasks',
			p_user: BORJA_ID,
			p_collective: COLLECTIVE_ID
		});
		expect(borjaEval.error).toBeNull();
		expect(borjaEval.data).toBe(false);

		// Carmen (also a member of the same collective) still sees tasks ON.
		const carmen = await createClientAs(CARMEN_ID);
		const carmenEval = await carmen.rpc('section_enabled', {
			p_section: 'tasks',
			p_user: CARMEN_ID,
			p_collective: COLLECTIVE_ID
		});
		expect(carmenEval.error).toBeNull();
		expect(carmenEval.data).toBe(true);
	});

	it('SV-03: collective OFF beats user ON (admin override applies to everyone)', async () => {
		await resetFlags();

		// Carmen tries to opt INTO notes for herself.
		const carmen = await createClientAs(CARMEN_ID);
		const carmenSet = await carmen
			.from('users')
			.update({ feature_flags: { notes: true } })
			.eq('id', CARMEN_ID);
		expect(carmenSet.error).toBeNull();

		// Ana (admin) turns notes OFF for the whole collective.
		const ana = await createClientAs(ANA_ID);
		const collSet = await ana
			.from('collectives')
			.update({ feature_flags: { notes: false } })
			.eq('id', COLLECTIVE_ID);
		expect(collSet.error).toBeNull();

		const result = await carmen.rpc('section_enabled', {
			p_section: 'notes',
			p_user: CARMEN_ID,
			p_collective: COLLECTIVE_ID
		});
		expect(result.error).toBeNull();
		expect(result.data).toBe(false);
	});
});

describe('feature_flags RLS writes', () => {
	it('SV-04: a user can update their own feature_flags', async () => {
		await resetFlags();
		const borja = await createClientAs(BORJA_ID);
		const { error } = await borja
			.from('users')
			.update({ feature_flags: { search: false } })
			.eq('id', BORJA_ID);
		expect(error).toBeNull();

		const { data } = await borja
			.from('users')
			.select('feature_flags')
			.eq('id', BORJA_ID)
			.single();
		expect(data?.feature_flags).toEqual({ search: false });
	});

	it('SV-05: admin can update collective feature_flags; non-admin member cannot', async () => {
		await resetFlags();

		// Ana (admin) — allowed.
		const ana = await createClientAs(ANA_ID);
		const anaUpd = await ana
			.from('collectives')
			.update({ feature_flags: { tasks: false } })
			.eq('id', COLLECTIVE_ID)
			.select('feature_flags');
		expect(anaUpd.error).toBeNull();
		expect(anaUpd.data?.[0]?.feature_flags).toEqual({ tasks: false });

		// Borja (member, not admin) — denied. RLS UPDATE policy returns 0 rows
		// updated; PostgREST surfaces this as `data: []` with no error.
		const borja = await createClientAs(BORJA_ID);
		const borjaUpd = await borja
			.from('collectives')
			.update({ feature_flags: { tasks: true } })
			.eq('id', COLLECTIVE_ID)
			.select('feature_flags');
		expect(borjaUpd.data ?? []).toHaveLength(0);

		// The collective row still reflects Ana's value (admin's call won).
		const { data: postState } = await admin
			.from('collectives')
			.select('feature_flags')
			.eq('id', COLLECTIVE_ID)
			.single();
		expect(postState?.feature_flags).toEqual({ tasks: false });
	});
});

describe('per-collective isolation', () => {
	it('SV-06: a user override applied to one collective context is independent of another (where Ana is also a member)', async () => {
		await resetFlags();

		// Build a second collective that Ana is admin of.
		const { data: extra, error: createErr } = await admin
			.from('collectives')
			.insert({
				name: `SV-06 extra ${Date.now()}`,
				emoji: '🧪',
				created_by: ANA_ID
			})
			.select('id')
			.single();
		expect(createErr).toBeNull();
		const extraId = extra!.id;
		createdCollectiveIds.push(extraId);

		await admin
			.from('collective_members')
			.insert({ collective_id: extraId, user_id: ANA_ID, role: 'admin' });

		// Seed collective: collective OFF tasks. Extra collective: collective ON
		// (empty / no opinion) tasks. Ana's user flags are empty.
		await admin
			.from('collectives')
			.update({ feature_flags: { tasks: false } })
			.eq('id', COLLECTIVE_ID);
		await admin
			.from('collectives')
			.update({ feature_flags: {} })
			.eq('id', extraId);

		const ana = await createClientAs(ANA_ID);

		const seedEval = await ana.rpc('section_enabled', {
			p_section: 'tasks',
			p_user: ANA_ID,
			p_collective: COLLECTIVE_ID
		});
		expect(seedEval.error).toBeNull();
		expect(seedEval.data).toBe(false);

		const extraEval = await ana.rpc('section_enabled', {
			p_section: 'tasks',
			p_user: ANA_ID,
			p_collective: extraId
		});
		expect(extraEval.error).toBeNull();
		expect(extraEval.data).toBe(true);
	});
});
