/**
 * EditorView save-and-deploy: a failed PUT must abort the deploy. Verified by
 * intercepting the PUT with a forced 500 and asserting that no POST to /deploy
 * is observed, plus a "Failed to save file" toast surfaces.
 *
 * Empty-stack create opens an editable compose workspace immediately
 * (startInComposeEdit), so this spec waits for Save & Deploy rather than
 * clicking Anatomy "Edit compose".
 */
import { test, expect } from '@playwright/test';
import { loginAs, waitForStacksLoaded } from './helpers';

const TEST_STACK = 'e2e-save-deploy-stack';

async function deleteTestStack(page: import('@playwright/test').Page) {
  await page.evaluate(async (name) => {
    await fetch(`/api/stacks/${name}`, { method: 'DELETE', credentials: 'include' }).catch(() => { });
  }, TEST_STACK);
}

async function createTestStack(page: import('@playwright/test').Page) {
  await page.getByRole('button', { name: 'Create Stack' }).click();
  await expect(page.getByRole('dialog')).toBeVisible({ timeout: 5_000 });
  await page.locator('#create-stack-name').fill(TEST_STACK);
  await page.locator('[role="dialog"]').getByRole('button', { name: 'Create' }).click();
  await expect(page.getByRole('dialog')).toBeHidden({ timeout: 8_000 });
  // Empty create auto-opens compose edit; wait for that before asserting.
  await expect(page.getByRole('button', { name: 'Save & Deploy', exact: true })).toBeVisible({ timeout: 10_000 });
}

test.describe('EditorView save-and-deploy', () => {
  test.beforeEach(async ({ page }) => {
    await loginAs(page);
    await waitForStacksLoaded(page);
    await deleteTestStack(page);
    await page.waitForTimeout(300);
    await page.reload();
    await loginAs(page);
    await waitForStacksLoaded(page);
    await createTestStack(page);
  });

  test.afterEach(async ({ page }) => {
    await deleteTestStack(page);
  });

  test('does NOT deploy when the save PUT fails', async ({ page }) => {
    // Force the compose-save PUT to fail.
    await page.route(`**/api/stacks/${TEST_STACK}`, async (route, req) => {
      if (req.method() === 'PUT') {
        await route.fulfill({ status: 500, body: 'forced save failure' });
        return;
      }
      await route.continue();
    });

    // Track whether the deploy POST is ever attempted.
    let deployAttempts = 0;
    await page.route(`**/api/stacks/${TEST_STACK}/deploy*`, async (route, req) => {
      if (req.method() === 'POST') deployAttempts += 1;
      await route.continue();
    });

    // No need to modify Monaco content: saveFile fires the PUT regardless of
    // dirty state. The route interceptor forces it to 500; the gated handler
    // then must not call POST /deploy.
    await page.getByRole('button', { name: 'Save & Deploy', exact: true }).click();

    // Failure toast must appear.
    await expect(page.getByText(/failed to save file/i)).toBeVisible({ timeout: 5_000 });

    // Give the UI a beat to (incorrectly) fire a deploy if the guard is broken.
    await page.waitForTimeout(1_000);
    expect(deployAttempts).toBe(0);
  });
});
