// End-to-end smoke: bootstrap → accept pending host → run backup → see succeeded.
//
// The compose stack stands up a server, a sibling rest-server, and an
// agent in announce-and-approve mode. This test drives the operator
// path through the UI (login + dashboard) and the API
// (accept + run-now + poll for terminal) — UI for the human surfaces,
// API for the deterministic ones.

import { test, expect } from '@playwright/test';
import {
    baseURL,
    bootstrapAdmin,
    loginViaUI,
    waitForPendingHostID,
    acceptPending,
    waitForHostStatus,
    createSourceGroup,
    runSourceGroup,
    getSessionCookie,
} from './lib/server';

test.describe('smoke: enrol-via-announce → backup', () => {
    test('happy path: enrol → accept → backup → succeeded', async ({ page, request }) => {
        const { username, password } = await bootstrapAdmin(request);
        await loginViaUI(page, username, password);

        // Dashboard renders.
        await expect(page.locator('main')).toContainText(/host|fleet|pending/i, { timeout: 10_000 });

        // Pending host appears (the agent container has been
        // announcing since startup).
        const pendingID = await waitForPendingHostID(page);
        const cookie = await getSessionCookie(page);

        // Accept with the rest-server creds. compose's rest-server runs
        // --no-auth, so any credentials work; restic still demands a
        // password to encrypt the repo.
        await acceptPending(request, cookie, pendingID, {
            url: 'rest:http://rest-server:8000/',
            password: 'e2e-repo-password',
        });

        // Wait for the host to come online AND for auto-init to
        // finish. Coming online happens as soon as the agent's
        // bearer-authed WS attaches (~1s after accept); repo_status
        // flips to 'ready' once the auto-init job completes (a
        // couple of seconds later). Loading the host page before
        // that leaves the Run-backup button disabled because the
        // server-rendered HTML reflects the still-in-progress init,
        // and the page has no live-refresh on that field.
        const readyHost = await waitForHostStatus(
            request, cookie,
            (h) => h.status === 'online' && h.repo_status === 'ready',
            90_000,
        );
        expect(readyHost.id).toBeTruthy();

        // Per-host Run-now is gone; backups are dispatched per
        // source-group now. Create one that maps to the agent's
        // /source mount, then kick it via the JSON API.
        const groupID = await createSourceGroup(request, cookie, readyHost.id, {
            name: 'default',
            includes: ['/source'],
        });
        await runSourceGroup(request, cookie, readyHost.id, groupID);

        // Wait for the host's last_backup_status to flip to 'succeeded'.
        // The host record is the source of truth: it's what the
        // dashboard projects from job-completion events on the WS
        // channel.
        const finishedHost = await waitForHostStatus(
            request, cookie,
            (h) => h.id === readyHost.id && h.last_backup_status === 'succeeded',
            120_000,
        );
        expect(finishedHost.last_backup_status).toBe('succeeded');
    });
});

test.describe('smoke: scrape /metrics', () => {
    test('metrics endpoint exposes the host gauge', async ({ request }) => {
        // Compose sets RM_METRICS_TRUSTED_CIDR=0.0.0.0/0 so the
        // endpoint is open to the test runner.
        const res = await request.get(`${baseURL}/metrics`);
        expect(res.status()).toBe(200);
        const body = await res.text();
        expect(body).toContain('rm_hosts_total');
        expect(body).toContain('rm_build_info{');
    });
});
