import { expect, test, type Page } from "@playwright/test";
import { execFileSync } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import { resolve } from "node:path";

const ADMIN_EMAIL = "admin@capakraken.dev";
const ADMIN_PASSWORD = "admin123";
const CURRENT_CONVERSATION_ID = "assistant-e2e-current";
const DB_WORKDIR = resolve(process.cwd(), "../../packages/db");
const WEB_ENV_PATHS = [
  resolve(process.cwd(), ".env.local"),
  resolve(process.cwd(), "apps/web/.env.local"),
];

function resolveDatabaseUrl(): string {
  const explicitPlaywrightUrl = process.env["PLAYWRIGHT_DATABASE_URL"];
  if (explicitPlaywrightUrl) {
    return explicitPlaywrightUrl;
  }

  for (const envPath of WEB_ENV_PATHS) {
    if (!existsSync(envPath)) {
      continue;
    }

    const envFile = readFileSync(envPath, "utf8");
    for (const rawLine of envFile.split(/\r?\n/u)) {
      const line = rawLine.trim();
      if (!line || line.startsWith("#")) {
        continue;
      }
      if (line.startsWith("PLAYWRIGHT_DATABASE_URL=")) {
        return line.slice("PLAYWRIGHT_DATABASE_URL=".length);
      }
      if (line.startsWith("DATABASE_URL_TEST=")) {
        return line.slice("DATABASE_URL_TEST=".length);
      }
      if (line.startsWith("DATABASE_URL=")) {
        return line.slice("DATABASE_URL=".length);
      }
    }
  }

  const fallbackTestUrl = process.env["DATABASE_URL_TEST"];
  if (fallbackTestUrl) {
    return fallbackTestUrl;
  }

  const fallback = process.env["DATABASE_URL"];
  if (fallback) {
    return fallback;
  }

  throw new Error("DATABASE_URL is not available for the Playwright assistant approval test.");
}

function runDbJson<T>(body: string): T {
  const script = `
    import { PrismaClient } from "@prisma/client";
    const prisma = new PrismaClient({
      datasources: {
        db: {
          url: process.env.DATABASE_URL,
        },
      },
    });

    try {
      ${body}
    } finally {
      await prisma.$disconnect();
    }
  `;

  const output = execFileSync("node", ["--input-type=module", "-e", script], {
    cwd: DB_WORKDIR,
    env: {
      ...process.env,
      DATABASE_URL: resolveDatabaseUrl(),
    },
    encoding: "utf8",
  }).trim();

  return JSON.parse(output) as T;
}

function runDb(body: string): void {
  runDbJson<null>(`${body}\nconsole.log("null");`);
}

async function signInAsAdmin(page: Page) {
  await page.goto("/auth/signin");
  await page.fill('input[type="email"]', ADMIN_EMAIL);
  await page.fill('input[type="password"]', ADMIN_PASSWORD);
  await page.click('button[type="submit"]');
  await expect(page).toHaveURL(/\/(dashboard|resources)/);
}

test.describe("Assistant approvals", () => {
  test.describe.configure({ mode: "serial" });

  test.beforeEach(async ({ page }) => {
    await page.addInitScript((conversationId) => {
      window.sessionStorage.setItem("capakraken-chat-conversation-id", conversationId);
    }, CURRENT_CONVERSATION_ID);

    runDb(`
      await prisma.systemSettings.upsert({
        where: { id: "singleton" },
        update: {
          aiProvider: "openai",
          azureOpenAiApiKey: "e2e-dummy-key",
          azureOpenAiDeployment: "gpt-4o-mini",
        },
        create: {
          id: "singleton",
          aiProvider: "openai",
          azureOpenAiApiKey: "e2e-dummy-key",
          azureOpenAiDeployment: "gpt-4o-mini",
        },
      });

      const admin = await prisma.user.findUniqueOrThrow({
        where: { email: ${JSON.stringify(ADMIN_EMAIL)} },
        select: { id: true },
      });

      await prisma.assistantApproval.deleteMany({
        where: { userId: admin.id },
      });

      await prisma.client.deleteMany({
        where: {
          name: {
            startsWith: "E2E Approval Client",
          },
        },
      });
    `);
  });

  test.afterEach(async () => {
    runDb(`
      await prisma.assistantApproval.deleteMany({
        where: {
          conversationId: {
            startsWith: "assistant-e2e-",
          },
        },
      });

      await prisma.client.deleteMany({
        where: {
          name: {
            startsWith: "E2E Approval Client",
          },
        },
      });
    `);
  });

  test("renders the pending approval inbox and handles cross-conversation actions", async ({ page }) => {
    const suffix = Date.now();
    const currentClientName = `E2E Approval Client Current ${suffix}`;
    const otherClientName = `E2E Approval Client Other ${suffix}`;
    const otherConversationId = `assistant-e2e-other-${suffix}`;

    const { currentApproval, otherApproval } = runDbJson<{
      currentApproval: { id: string; summary: string };
      otherApproval: { id: string; summary: string };
    }>(`
      const admin = await prisma.user.findUniqueOrThrow({
        where: { email: ${JSON.stringify(ADMIN_EMAIL)} },
        select: { id: true },
      });

      const currentApproval = await prisma.assistantApproval.create({
        data: {
          userId: admin.id,
          conversationId: ${JSON.stringify(CURRENT_CONVERSATION_ID)},
          toolName: "create_client",
          toolArguments: ${JSON.stringify(JSON.stringify({ name: currentClientName }))},
          summary: ${JSON.stringify(`create client (name=${currentClientName})`)},
          expiresAt: new Date(Date.now() + 15 * 60 * 1000),
        },
        select: { id: true, summary: true },
      });

      const otherApproval = await prisma.assistantApproval.create({
        data: {
          userId: admin.id,
          conversationId: ${JSON.stringify(otherConversationId)},
          toolName: "create_client",
          toolArguments: ${JSON.stringify(JSON.stringify({ name: otherClientName }))},
          summary: ${JSON.stringify(`create client (name=${otherClientName})`)},
          expiresAt: new Date(Date.now() + 15 * 60 * 1000),
        },
        select: { id: true, summary: true },
      });

      console.log(JSON.stringify({ currentApproval, otherApproval }));
    `);

    await signInAsAdmin(page);
    await page.goto("/dashboard");
    await page.getByTestId("assistant-open-button").click();

    await expect(page.getByRole("heading", { name: "HartBOT" })).toBeVisible();
    await expect(page.getByTestId("assistant-open-approvals")).toBeVisible();
    await expect(page.getByText(currentApproval.summary)).toBeVisible();
    await expect(page.getByText(otherApproval.summary)).toBeVisible();

    const currentCard = page.locator('[data-testid="assistant-approval-card"][data-conversation-scope="current"]').first();
    const otherCard = page.locator('[data-testid="assistant-approval-card"][data-conversation-scope="other"]').first();
    await expect(currentCard).toContainText("This chat");
    await expect(otherCard).toContainText("Other chat");

    await otherCard.getByTestId("assistant-approval-cancel").click();
    await expect(page.getByText(`Aktion verworfen: ${otherApproval.summary}`)).toBeVisible();
    await expect(page.locator(`[data-testid="assistant-approval-card"][data-approval-id="${otherApproval.id}"]`)).toHaveCount(0);

    await expect
      .poll(async () => {
        return runDbJson<string | null>(`
          const approval = await prisma.assistantApproval.findUnique({
            where: { id: ${JSON.stringify(otherApproval.id)} },
            select: { status: true },
          });
          console.log(JSON.stringify(approval?.status ?? null));
        `);
      })
      .toBe("CANCELLED");

    await currentCard.getByTestId("assistant-approval-confirm").click();

    await expect(page.getByText(`Ausgeführt: Created client: ${currentClientName}`)).toBeVisible();
    await expect(page.getByText(currentApproval.summary)).toBeVisible();

    await expect
      .poll(async () => {
        return runDbJson<string | null>(`
          const approval = await prisma.assistantApproval.findUnique({
            where: { id: ${JSON.stringify(currentApproval.id)} },
            select: { status: true },
          });
          console.log(JSON.stringify(approval?.status ?? null));
        `);
      })
      .toBe("APPROVED");

    await expect
      .poll(async () => {
        return runDbJson<"created" | "missing">(`
          const client = await prisma.client.findFirst({
            where: { name: ${JSON.stringify(currentClientName)} },
            select: { id: true },
          });
          console.log(JSON.stringify(client ? "created" : "missing"));
        `);
      })
      .toBe("created");

    await expect(page.getByTestId("assistant-approval-card")).toHaveCount(0);
  });
});
