import type { APIRequestContext } from '@playwright/test';
import { adminApi } from './api';

// Resources to delete after a test, in LIFO order. Tests register
// their creations and the registry tears everything down in
// `run()` — typically called from `test.afterEach`.
//
// A non-2xx status (other than 404) is treated as a real failure and
// logged to stderr. The previous shape silently swallowed every
// error, so a backend that started returning 500 on cleanup would
// have leaked orphans invisibly across runs. 404 stays tolerated —
// the test may have already deleted the resource itself.

interface CleanupItem {
	label: string;
	path: string;
}

export class CleanupRegistry {
	private items: CleanupItem[] = [];

	app(slugOrId: string): void {
		this.items.push({
			label: `app=${slugOrId}`,
			path: `/api/v1/admin/apps/${encodeURIComponent(slugOrId)}?force=true`
		});
	}

	adminUser(userId: string): void {
		this.items.push({
			label: `admin=${userId}`,
			path: `/api/v1/admin/admins/${userId}`
		});
	}

	apiKey(keyId: string): void {
		this.items.push({
			label: `key=${keyId}`,
			path: `/api/v1/admin/api-keys/${keyId}`
		});
	}

	async run(): Promise<void> {
		if (this.items.length === 0) return;
		const api = await adminApi();
		try {
			// Copy-then-reverse so a defensive double-`run()` (or a
			// caller that inspects the registry after a partial
			// teardown) doesn't see the items in a re-reversed order.
			for (const item of [...this.items].reverse()) {
				await deleteAndReport(api, item);
			}
		} finally {
			await api.dispose();
			this.items = [];
		}
	}
}

async function deleteAndReport(
	api: APIRequestContext,
	item: CleanupItem
): Promise<void> {
	try {
		const res = await api.delete(item.path);
		// 2xx and 404 are both "this resource is no longer here" — fine.
		if (!res.ok() && res.status() !== 404) {
			console.warn(
				`[cleanup] ${item.label} failed: HTTP ${res.status()} ${await res.text()}`
			);
		}
	} catch (err) {
		// Network-level failure (request never reached the server,
		// timeout, etc.). Log so a leak doesn't accumulate silently.
		console.warn(`[cleanup] ${item.label} failed: ${(err as Error).message}`);
	}
}
