import fs from "fs";
import path from "path";
import { spawnSync } from "child_process";
import { chromium } from "playwright";

const baseUrl = process.env.SQUAREMCP_BASE_URL || "https://squaremcp.com";
const profile = process.env.SQUAREMCP_E2E_PROFILE || "desktop";
const screenshotPath =
  process.env.SQUAREMCP_E2E_SCREENSHOT || `/tmp/squaremcp-e2e-${profile}.png`;
const baselinePath =
  process.env.SQUAREMCP_E2E_BASELINE ||
  path.join(process.cwd(), "product/site/baselines", `${profile}.png`);
const diffPath =
  process.env.SQUAREMCP_E2E_DIFF || `/tmp/squaremcp-e2e-${profile}-diff.png`;
const vaultRoot = process.env.OBSIDIAN_VAULT_PATH || "/home/garfield/obsidian/vaults";

function assert(condition, message) {
  if (!condition) {
    throw new Error(message);
  }
}

function uniqueEmail() {
  const stamp = Date.now();
  return `squaremcp-e2e-${profile}-${stamp}@example.com`;
}

function getProfileSettings() {
  if (profile === "mobile") {
    return {
      viewport: { width: 390, height: 1180 },
      isMobile: true,
      hasTouch: true,
    };
  }

  return {
    viewport: { width: 1440, height: 1400 },
    isMobile: false,
    hasTouch: false,
  };
}

async function runMobileLayoutChecks(page) {
  const layout = await page.evaluate(() => {
    const selectors = [
      ".topbar-row",
      ".topbar-actions",
      "h1",
      ".lede",
      ".hero-points",
      ".actions",
      ".hero-panel",
      "#pilot-form",
    ];

    const viewportWidth = window.innerWidth;
    const viewportHeight = window.innerHeight;
    const scrollWidth = document.documentElement.scrollWidth;
    const issues = [];

    for (const selector of selectors) {
      const element = document.querySelector(selector);
      if (!element) {
        issues.push(`missing element: ${selector}`);
        continue;
      }

      const rect = element.getBoundingClientRect();
      if (rect.left < -1 || rect.right > viewportWidth + 1) {
        issues.push(`horizontal overflow: ${selector}`);
      }
      if (rect.width <= 0 || rect.height <= 0) {
        issues.push(`collapsed element: ${selector}`);
      }
    }

    const actionButtons = Array.from(document.querySelectorAll(".actions .button"));
    if (actionButtons.length >= 2) {
      const first = actionButtons[0].getBoundingClientRect();
      const second = actionButtons[1].getBoundingClientRect();
      const overlap =
        first.left < second.right &&
        first.right > second.left &&
        first.top < second.bottom &&
        first.bottom > second.top;
      if (overlap) {
        issues.push("action buttons overlap");
      }
    }

    return {
      scrollWidth,
      viewportWidth,
      viewportHeight,
      issues,
    };
  });

  assert(
    layout.scrollWidth <= layout.viewportWidth + 1,
    `mobile horizontal overflow detected (${layout.scrollWidth} > ${layout.viewportWidth})`
  );
  assert(layout.issues.length === 0, `mobile layout issues: ${layout.issues.join(", ")}`);
}

function getEasternDateString() {
  return new Intl.DateTimeFormat("en-CA", {
    timeZone: "America/New_York",
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  }).format(new Date());
}

async function waitForVaultWrite(requestId) {
  const pilotLogPath = path.join(vaultRoot, "SquareMCP", "Pilot Requests.md");
  const dailyNotePath = path.join(vaultRoot, "Daily Notes", `${getEasternDateString()}.md`);
  const timeoutMs = 10000;
  const intervalMs = 250;
  const deadline = Date.now() + timeoutMs;

  while (Date.now() < deadline) {
    const pilotLog = fs.existsSync(pilotLogPath) ? fs.readFileSync(pilotLogPath, "utf8") : "";
    const dailyLog = fs.existsSync(dailyNotePath) ? fs.readFileSync(dailyNotePath, "utf8") : "";

    if (pilotLog.includes(requestId) && dailyLog.includes(requestId)) {
      return { pilotLogPath, dailyNotePath };
    }

    await new Promise((resolve) => setTimeout(resolve, intervalMs));
  }

  throw new Error(`vault write not observed for request id ${requestId}`);
}

async function run() {
  const browser = await chromium.launch({
    headless: true,
    args: [
      "--host-resolver-rules=MAP squaremcp.com 104.190.60.129,MAP www.squaremcp.com 104.190.60.129",
    ],
  });
  const context = await browser.newContext(getProfileSettings());
  const page = await context.newPage();

  try {
    const response = await page.goto(baseUrl, { waitUntil: "networkidle" });
    assert(response && response.ok(), "page did not load successfully");
    assert((await page.title()) === "SquareMCP", "page title mismatch");

    await page.click('a.topbar-link[href="#pricing"]');
    await page.waitForFunction(() => window.location.hash === "#pricing");

    await page.click('a.topbar-link[href="#pilot-form"]');
    await page.waitForFunction(() => window.location.hash === "#pilot-form");

    if (profile === "mobile") {
      await runMobileLayoutChecks(page);
    }

    const heroImage = page.locator("#heroAnimation");
    await heroImage.waitFor();
    const initialSrc = await heroImage.getAttribute("src");
    assert(
      initialSrc && initialSrc.includes("squaremcp_launch.gif"),
      "hero image did not start with animated asset"
    );

    const playMs = Number((await heroImage.getAttribute("data-play-ms")) || "0");
    assert(playMs > 0, "hero animation play duration missing");
    await page.waitForTimeout(playMs + 750);
    const finalSrc = await heroImage.getAttribute("src");
    assert(
      finalSrc && finalSrc.includes("squaremcp_launch_poster.png"),
      "hero image did not swap to poster"
    );

    await page.screenshot({ path: screenshotPath, fullPage: true });
    if (fs.existsSync(baselinePath)) {
      const compare = spawnSync(
        process.execPath,
        ["product/site/compare-screenshot.mjs", screenshotPath, baselinePath, diffPath, "0.02", "0.015"],
        { stdio: "inherit" }
      );
      assert(compare.status === 0, `visual diff failed for ${profile}`);
    }

    const email = uniqueEmail();
    await page.fill('input[name="name"]', `SquareMCP ${profile.toUpperCase()} E2E`);
    await page.fill('input[name="email"]', email);
    await page.fill('input[name="company"]', `SquareMCP ${profile} Browser Test`);
    await page.fill('input[name="role"]', "QA");
    await page.evaluate(() => {
      const tagField = document.querySelector('input[name="submission_tag"]');
      if (tagField) {
        tagField.value = "#squaremcp-e2e #squaremcp-test-cleanup";
      }
    });
    await page.selectOption('select[name="use_case"]', { label: "Internal support copilot" });
    await page.selectOption('select[name="timeline"]', { label: "Within 2 weeks" });
    await page.fill('textarea[name="systems"]', "Postgres, internal REST APIs");
    await page.fill('textarea[name="requirements"]', "Audit logs and SSO");

    const submitResponsePromise = page.waitForResponse(
      (resp) => resp.url().includes("/api/pilot-request") && resp.request().method() === "POST"
    );
    await page.click('button[type="submit"]');
    const submitResponse = await submitResponsePromise;
    assert(submitResponse.ok(), `pilot submit failed with status ${submitResponse.status()}`);
    const submitJson = await submitResponse.json();
    assert(submitJson.ok === true, "submit response missing ok=true");
    assert(
      typeof submitJson.request_id === "string" && submitJson.request_id.length > 0,
      "submit response missing request id"
    );

    const outputText = await page.locator("#pilotOutput").textContent();
    assert(
      outputText && outputText.includes("Saved to SquareMCP intake successfully."),
      "success message missing"
    );
    assert(outputText.includes(submitJson.request_id), "success message missing request id");

    const nameValue = await page.locator('input[name="name"]').inputValue();
    assert(nameValue === "", "form did not reset after submit");

    const vaultTargets = await waitForVaultWrite(submitJson.request_id);
    const pilotLog = fs.readFileSync(vaultTargets.pilotLogPath, "utf8");
    const dailyLog = fs.readFileSync(vaultTargets.dailyNotePath, "utf8");
    assert(
      pilotLog.includes("#squaremcp-e2e #squaremcp-test-cleanup"),
      "pilot log missing cleanup tag"
    );
    assert(
      dailyLog.includes("#squaremcp-e2e #squaremcp-test-cleanup"),
      "daily log missing cleanup tag"
    );

    console.log(`squaremcp product site e2e test (${profile}): PASS`);
    console.log(`request_id: ${submitJson.request_id}`);
    console.log(`email: ${email}`);
    console.log(`screenshot: ${screenshotPath}`);
    console.log(`baseline: ${baselinePath}`);
    console.log(`diff: ${diffPath}`);
    console.log(`pilot_log: ${vaultTargets.pilotLogPath}`);
    console.log(`daily_log: ${vaultTargets.dailyNotePath}`);
  } finally {
    await context.close();
    await browser.close();
  }
}

run().catch((error) => {
  console.error(`squaremcp product site e2e test (${profile}): FAIL: ${error.message}`);
  process.exit(1);
});
