import { expect, type Page } from "@playwright/test";
import { testSkipIfWindows, Timeout } from "./helpers/test_helper";
const fs = require("fs");
const path = require("path");

async function saveVisualChanges(page: Page) {
  await expect(async () => {
    const saveButton = page.getByRole("button", { name: "Save Changes" });
    await expect(saveButton).toBeVisible({ timeout: 1_000 });
    await expect(saveButton).toBeEnabled({ timeout: 1_000 });
    await saveButton.click();
  }).toPass({ timeout: Timeout.MEDIUM });
}

testSkipIfWindows("edit style of one selected component", async ({ po }) => {
  await po.setUpDyadPro();
  await po.sendPrompt("tc=basic");
  await po.previewPanel.clickTogglePreviewPanel();
  await po.previewPanel.clickPreviewPickElement();

  // Select a component
  await po.previewPanel
    .getPreviewIframeElement()
    .contentFrame()
    .getByRole("heading", { name: "Welcome to Your Blank App" })
    .click();

  // Wait for the toolbar to appear (check for the Margin button which is always visible)
  const marginButton = po.page.getByRole("button", { name: "Margin" });
  await expect(marginButton).toBeVisible({
    timeout: Timeout.MEDIUM,
  });

  // Ensure the toolbar has proper coordinates before clicking
  await expect(async () => {
    const box = await marginButton.boundingBox();
    expect(box).not.toBeNull();
    expect(box!.y).toBeGreaterThan(0);
  }).toPass({ timeout: Timeout.MEDIUM });

  // Click on margin button to open the margin popover
  await marginButton.click();

  // Wait for the popover to fully open by checking for the popover content container
  const marginDialog = po.page
    .locator('[role="dialog"]')
    .filter({ hasText: "Margin" });
  await expect(marginDialog).toBeVisible({
    timeout: Timeout.LONG,
  });

  // Edit margin - set horizontal margin
  const marginXInput = po.page.getByLabel("Horizontal");
  await marginXInput.fill("20");

  // Edit margin - set vertical margin
  const marginYInput = po.page.getByLabel("Vertical");
  await marginYInput.fill("10");

  // Close the popover by clicking outside or pressing escape
  await po.page.keyboard.press("Escape");

  // Check if the changes are applied to UI by verifying the visual changes dialog appears
  await expect(po.page.getByText(/\d+ component[s]? modified/)).toBeVisible({
    timeout: Timeout.MEDIUM,
  });

  // Save the changes
  await saveVisualChanges(po.page);

  // Wait for the success toast
  await po.toastNotifications.waitForToastWithText(
    "Visual changes saved to source files",
  );

  // Verify that the changes are applied to codebase
  await po.snapshotAppFiles({
    name: "visual-editing-single-component-margin",
    files: ["src/pages/Index.tsx"],
  });
});

testSkipIfWindows("edit text of the selected component", async ({ po }) => {
  await po.setUpDyadPro();
  await po.sendPrompt("tc=basic");
  await po.previewPanel.clickTogglePreviewPanel();
  await po.previewPanel.clickPreviewPickElement();

  // Click on component that contains static text
  await po.previewPanel
    .getPreviewIframeElement()
    .contentFrame()
    .getByRole("heading", { name: "Welcome to Your Blank App" })
    .click();

  // Wait for the toolbar to appear (check for the Margin button which is always visible)
  await expect(po.page.getByRole("button", { name: "Margin" })).toBeVisible({
    timeout: Timeout.MEDIUM,
  });

  // Get the iframe and access the content
  const iframe = po.previewPanel.getPreviewIframeElement();
  const frame = iframe.contentFrame();

  // Find the heading element in the iframe
  const heading = frame.getByRole("heading", {
    name: "Welcome to Your Blank App",
  });

  await heading.dblclick();

  // Wait for contentEditable to be enabled
  await expect(async () => {
    const isEditable = await heading.evaluate(
      (el) => (el as HTMLElement).isContentEditable,
    );
    expect(isEditable).toBe(true);
  }).toPass({ timeout: Timeout.MEDIUM });

  // Clear the existing text and type new text
  await heading.press("Meta+A");
  await heading.type("Hello from E2E Test");

  // Click outside to finish editing
  await frame.locator("body").click({ position: { x: 10, y: 10 } });

  // Verify the changes are applied in the UI
  await expect(frame.getByText("Hello from E2E Test")).toBeVisible({
    timeout: Timeout.MEDIUM,
  });

  // Verify the visual changes dialog appears
  await expect(po.page.getByText(/\d+ component[s]? modified/)).toBeVisible({
    timeout: Timeout.MEDIUM,
  });

  // Save the changes
  await saveVisualChanges(po.page);

  // Wait for the success toast
  await po.toastNotifications.waitForToastWithText(
    "Visual changes saved to source files",
  );

  // Verify that the changes are applied to the codebase
  await po.snapshotAppFiles({
    name: "visual-editing-text-content",
    files: ["src/pages/Index.tsx"],
  });
});

testSkipIfWindows("swap image via URL", async ({ po }) => {
  await po.setUpDyadPro();
  await po.sendPrompt("tc=image-basic");
  await po.approveProposal();

  // Wait for the app to rebuild with the new code
  await po.previewPanel.clickPreviewPickElement();

  // Wait for the image element to appear in the iframe after rebuild
  const heroImage = po.previewPanel
    .getPreviewIframeElement()
    .contentFrame()
    .getByRole("img", { name: "Hero image" });
  await expect(heroImage).toBeVisible({ timeout: Timeout.LONG });

  // Select the image element in the preview
  await heroImage.click();

  // Wait for the toolbar to appear (check for the Margin button which is always visible)
  const marginButton = po.page.getByRole("button", { name: "Margin" });
  await expect(marginButton).toBeVisible({
    timeout: Timeout.MEDIUM,
  });

  // Ensure the toolbar has proper coordinates before clicking
  await expect(async () => {
    const box = await marginButton.boundingBox();
    expect(box).not.toBeNull();
    expect(box!.y).toBeGreaterThan(0);
  }).toPass({ timeout: Timeout.MEDIUM });

  // Click the Swap Image button to open the image popover
  const swapImageButton = po.page.getByRole("button", { name: "Swap Image" });
  await expect(swapImageButton).toBeVisible({ timeout: Timeout.LONG });
  await swapImageButton.click();

  // Wait for the Image Source popover to appear
  const imagePopover = po.page
    .locator('[role="dialog"]')
    .filter({ hasText: "Image Source" });
  await expect(imagePopover).toBeVisible({
    timeout: Timeout.LONG,
  });

  // Enter a new image URL
  const urlInput = po.page.getByLabel("Image URL");
  await urlInput.fill("https://example.com/new-hero.png");

  // Click Apply to submit the new URL
  await po.page.getByRole("button", { name: "Apply" }).click();

  // Close the popover
  await po.page.keyboard.press("Escape");

  // Verify the visual changes dialog appears
  await expect(po.page.getByText(/\d+ component[s]? modified/)).toBeVisible({
    timeout: Timeout.MEDIUM,
  });

  // Save the changes
  await saveVisualChanges(po.page);

  // Wait for the success toast
  await po.toastNotifications.waitForToastWithText(
    "Visual changes saved to source files",
  );

  // Verify that the changes are applied to the codebase
  await po.snapshotAppFiles({
    name: "visual-editing-swap-image",
    files: ["src/pages/Index.tsx"],
  });
});

testSkipIfWindows("discard changes", async ({ po }) => {
  await po.setUpDyadPro();
  await po.sendPrompt("tc=basic");
  await po.previewPanel.clickTogglePreviewPanel();
  await po.previewPanel.clickPreviewPickElement();

  // Select a component
  await po.previewPanel
    .getPreviewIframeElement()
    .contentFrame()
    .getByRole("heading", { name: "Welcome to Your Blank App" })
    .click();

  // Wait for the toolbar to appear (check for the Margin button which is always visible)
  const marginButton = po.page.getByRole("button", { name: "Margin" });
  await expect(marginButton).toBeVisible({
    timeout: Timeout.MEDIUM,
  });

  // Ensure the toolbar has proper coordinates before clicking
  await expect(async () => {
    const box = await marginButton.boundingBox();
    expect(box).not.toBeNull();
    expect(box!.y).toBeGreaterThan(0);
  }).toPass({ timeout: Timeout.MEDIUM });

  // Click on margin button to open the margin popover
  await marginButton.click();

  // Wait for the popover to fully open by checking for the popover content container
  const marginDialog = po.page
    .locator('[role="dialog"]')
    .filter({ hasText: "Margin" });
  await expect(marginDialog).toBeVisible({
    timeout: Timeout.LONG,
  });

  // Edit margin
  const marginXInput = po.page.getByLabel("Horizontal");
  await marginXInput.fill("30");

  const marginYInput = po.page.getByLabel("Vertical");
  await marginYInput.fill("30");

  // Close the popover
  await po.page.keyboard.press("Escape");

  // Wait for the popover to close
  await expect(marginDialog).not.toBeVisible({
    timeout: Timeout.MEDIUM,
  });

  // Check if the changes are applied to UI
  await expect(po.page.getByText(/\d+ component[s]? modified/)).toBeVisible({
    timeout: Timeout.MEDIUM,
  });

  // Take a snapshot of the app files before discarding
  const appPathBefore = await po.appManagement.getCurrentAppPath();
  const appFileBefore = fs.readFileSync(
    path.join(appPathBefore, "src", "pages", "Index.tsx"),
    "utf-8",
  );

  // Discard the changes
  await po.page.getByRole("button", { name: "Discard" }).click();

  // Verify the visual changes dialog is gone
  await expect(po.page.getByText(/\d+ component[s]? modified/)).not.toBeVisible(
    {
      timeout: Timeout.MEDIUM,
    },
  );

  // Verify that the changes are NOT applied to codebase
  const appFileAfter = fs.readFileSync(
    path.join(appPathBefore, "src", "pages", "Index.tsx"),
    "utf-8",
  );

  // The file content should be the same as before
  expect(appFileAfter).toBe(appFileBefore);
});
