/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from "@playwright/test";
import { ChildProcess } from "child_process";
import fs from "fs";
import path from "path";
import type {
  TemplateEngine,
  TemplateFramework,
  TemplateType,
  TemplateUI,
} from "../templates";
import { createTestDir, runApp, runCreateLlama, type AppType } from "./utils";

const templateTypes: TemplateType[] = ["streaming", "simple"];
const templateFrameworks: TemplateFramework[] = [
  "nextjs",
  "express",
  "fastapi",
];
const templateEngines: TemplateEngine[] = ["simple", "context"];
const templateUIs: TemplateUI[] = ["shadcn", "html"];

for (const templateType of templateTypes) {
  for (const templateFramework of templateFrameworks) {
    for (const templateEngine of templateEngines) {
      for (const templateUI of templateUIs) {
        if (templateFramework === "nextjs" && templateType === "simple") {
          // nextjs doesn't support simple templates - skip tests
          continue;
        }
        if (templateEngine === "context") {
          // we don't test context templates because it needs OPEN_AI_KEY
          continue;
        }
        const appType: AppType =
          templateFramework === "express" || templateFramework === "fastapi"
            ? templateType === "simple"
              ? "--no-frontend" // simple templates don't have frontends
              : "--frontend"
            : "";
        if (appType === "--no-frontend" && templateUI !== "html") {
          // if there's no frontend, don't iterate over UIs
          continue;
        }
        test.describe(`try create-llama ${templateType} ${templateFramework} ${templateEngine} ${templateUI} ${appType}`, async () => {
          let port: number;
          let externalPort: number;
          let cwd: string;
          let name: string;
          let cps: ChildProcess[] = [];

          test.beforeAll(async () => {
            port = Math.floor(Math.random() * 10000) + 10000;
            externalPort = port + 1;

            cwd = await createTestDir();
            name = runCreateLlama(
              cwd,
              templateType,
              templateFramework,
              templateEngine,
              templateUI,
              appType,
              externalPort,
            );

            if (templateFramework !== "fastapi") {
              // don't run the app for fastapi for now (adds python dependency)
              cps = await runApp(cwd, name, appType, port, externalPort);
            }
          });

          test("App folder should exist", async () => {
            const dirExists = fs.existsSync(path.join(cwd, name));
            expect(dirExists).toBeTruthy();
          });
          test("Frontend should have a title", async ({ page }) => {
            test.skip(
              appType === "--no-frontend" || templateFramework === "fastapi",
            );
            await page.goto(`http://localhost:${port}`);
            await expect(page.getByText("Built by LlamaIndex")).toBeVisible();
          });

          test("Frontend should be able to submit a message and receive a response", async ({
            page,
          }) => {
            test.skip(
              appType === "--no-frontend" || templateFramework === "fastapi",
            );
            await page.goto(`http://localhost:${port}`);
            await page.fill("form input", "hello");
            await page.click("form button[type=submit]");
            const response = await page.waitForResponse(
              (res) => {
                return res.url().includes("/api/chat") && res.status() === 200;
              },
              {
                timeout: 1000 * 60,
              },
            );
            const text = await response.text();
            console.log("AI response when submitting message: ", text);
            expect(response.ok()).toBeTruthy();
          });

          test("Backend should response when calling API", async ({
            request,
          }) => {
            test.skip(
              appType !== "--no-frontend" || templateFramework === "fastapi",
            );
            const response = await request.post(
              `http://localhost:${port}/api/chat`,
              {
                data: {
                  messages: [
                    {
                      role: "user",
                      content: "Hello",
                    },
                  ],
                },
              },
            );
            const text = await response.text();
            console.log("AI response when calling API: ", text);
            expect(response.ok()).toBeTruthy();
          });

          // clean processes
          test.afterAll(async () => {
            cps.map((cp) => cp.kill());
          });
        });
      }
    }
  }
}