/**
 * MC-series: /collective/manage — admin UI flows (Fase 7.3).
 *
 * Ana is admin of the seed collective; Borja + Carmen are members; David is
 * guest. Tests run serially and restoreSeedMembership() in afterAll puts the
 * roster back to seed state so downstream suites (lists, items, tasks, notes)
 * see their expected roles.
 */
import { test, expect } from '@playwright/test';
import { USERS } from '../fixtures/users.js';
import { loginAs } from '../fixtures/login.js';
import { restoreSeedMembership } from '../fixtures/db.js';
import { closePool, sql, COLLECTIVE_ID } from '@colectivo/test-utils';

test.describe.configure({ mode: 'serial' });

test.describe('Manage collective (admin UI)', () => {
	test.afterAll(async () => {
		await restoreSeedMembership();
		await closePool();
	});

	test('MC-02: Ana promotes Borja from member to admin; change persists', async ({ page }) => {
		await loginAs(page, USERS.ana);
		await page.goto('/collective/manage');

		const borjaSelect = page.getByTestId(`role-select-${USERS.borja.id}`);
		await expect(borjaSelect).toBeVisible({ timeout: 15_000 });
		await expect(borjaSelect).toHaveValue('member');

		await borjaSelect.selectOption('admin');

		// DB updated.
		await expect
			.poll(
				async () => {
					const r = await sql(
						'SELECT role FROM public.collective_members WHERE collective_id = $1 AND user_id = $2',
						[COLLECTIVE_ID, USERS.borja.id]
					);
					return r.rows[0]?.role;
				},
				{ timeout: 5_000 }
			)
			.toBe('admin');

		// On reload, the UI reflects the new role.
		await page.reload();
		await expect(page.getByTestId(`role-select-${USERS.borja.id}`)).toHaveValue('admin', {
			timeout: 10_000
		});
	});

	test('MC-03: Ana demotes Borja back to member', async ({ page }) => {
		await loginAs(page, USERS.ana);
		await page.goto('/collective/manage');

		const borjaSelect = page.getByTestId(`role-select-${USERS.borja.id}`);
		await expect(borjaSelect).toBeVisible({ timeout: 15_000 });

		// Precondition from MC-02: Borja is admin. Fresh runs may start with
		// Borja as member — use a conditional so this test is order-independent.
		const currentRole = await borjaSelect.inputValue();
		if (currentRole !== 'admin') {
			await borjaSelect.selectOption('admin');
			await page.waitForTimeout(200);
		}

		await borjaSelect.selectOption('member');
		await expect
			.poll(async () => {
				const r = await sql(
					'SELECT role FROM public.collective_members WHERE collective_id = $1 AND user_id = $2',
					[COLLECTIVE_ID, USERS.borja.id]
				);
				return r.rows[0]?.role;
			})
			.toBe('member');
	});

	test('MC-04: Ana removes Carmen; the row disappears and membership row is gone', async ({
		page
	}) => {
		await loginAs(page, USERS.ana);
		await page.goto('/collective/manage');

		const carmenRow = page.getByTestId(`member-row-${USERS.carmen.id}`);
		await expect(carmenRow).toBeVisible({ timeout: 15_000 });

		await page.getByTestId(`remove-member-${USERS.carmen.id}`).click();
		await expect(carmenRow).toHaveCount(0, { timeout: 5_000 });

		const r = await sql(
			'SELECT count(*)::int AS n FROM public.collective_members WHERE collective_id = $1 AND user_id = $2',
			[COLLECTIVE_ID, USERS.carmen.id]
		);
		expect(r.rows[0].n).toBe(0);
	});

	test('MC-05: Ana generates a pending invitation; the token is visible + live', async ({
		page
	}) => {
		await loginAs(page, USERS.ana);
		await page.goto('/collective/manage');
		await expect(page.getByTestId(`role-select-${USERS.borja.id}`)).toBeVisible({
			timeout: 15_000
		});

		await page.getByTestId('generate-invite').click();
		const link = page.getByTestId('invite-link');
		await expect(link).toBeVisible({ timeout: 5_000 });

		const url = (await link.textContent())?.trim() ?? '';
		const token = url.split('/').pop()!;

		const r = await sql(
			`SELECT accepted_at, expires_at > now() AS active
			 FROM public.collective_invitations WHERE token = $1`,
			[token]
		);
		expect(r.rowCount).toBe(1);
		expect(r.rows[0].accepted_at).toBeNull();
		expect(r.rows[0].active).toBe(true);

		// Cleanup the generated row so downstream doesn't inherit it.
		await sql('DELETE FROM public.collective_invitations WHERE token = $1', [token]);
	});
});
