import { expect, test } from "@playwright/test";
import { gotoDashboardRoute } from "./helpers/dashboardAuth";

type ComboStub = {
  id: string;
  name: string;
  strategy: string;
  models: unknown[];
  config: Record<string, unknown>;
  isActive: boolean;
  sortOrder?: number;
};

type ComboCreatePayload = {
  name?: string;
  strategy?: string;
  models?: unknown[];
  config?: Record<string, unknown>;
};

async function dispatchHtml5DragAndDrop(
  page: import("@playwright/test").Page,
  source: import("@playwright/test").Locator,
  target: import("@playwright/test").Locator
) {
  const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
  await source.dispatchEvent("dragstart", { dataTransfer });
  await target.dispatchEvent("dragenter", { dataTransfer });
  await target.dispatchEvent("dragover", { dataTransfer });
  await target.dispatchEvent("drop", { dataTransfer });
  await source.dispatchEvent("dragend", { dataTransfer });
  await dataTransfer.dispose();
}

test.describe("Combos flow", () => {
  test("applies template, creates combo, and runs quick test CTA", async ({ page }) => {
    const state: {
      combos: ComboStub[];
      nextId: number;
      comboTestRequests: number;
    } = {
      combos: [],
      nextId: 1,
      comboTestRequests: 0,
    };

    await page.route("**/api/combos/test", async (route) => {
      state.comboTestRequests += 1;
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({
          resolvedBy: "openai/qa-test-model",
          results: [{ model: "openai/qa-test-model", status: "ok", latencyMs: 42 }],
        }),
      });
    });

    await page.route("**/api/combos/metrics", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ metrics: {} }),
      });
    });

    await page.route(/\/api\/settings$/, async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ comboConfigMode: "guided" }),
      });
    });

    await page.route("**/api/settings/proxy", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ combos: {} }),
      });
    });

    await page.route("**/api/providers", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({
          connections: [{ id: "conn-openai", provider: "openai", testStatus: "active" }],
        }),
      });
    });

    await page.route("**/api/provider-nodes", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ nodes: [] }),
      });
    });

    await page.route("**/api/models/alias", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ aliases: {} }),
      });
    });

    await page.route("**/api/provider-models", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({
          models: {
            openai: [{ id: "qa-test-model", name: "QA Test Model" }],
          },
        }),
      });
    });

    await page.route("**/api/pricing", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({
          openai: {
            "qa-test-model": {
              input: 0.01,
              output: 0.02,
            },
          },
        }),
      });
    });

    await page.route("**/api/combos/builder/options", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({
          providers: [
            {
              providerId: "openai",
              displayName: "OpenAI",
              connectionCount: 1,
              models: [{ id: "qa-test-model", name: "QA Test Model" }],
              connections: [
                {
                  id: "conn-openai",
                  label: "OpenAI Primary",
                  status: "active",
                  priority: 1,
                  defaultModel: "qa-test-model",
                },
              ],
            },
          ],
          comboRefs: [],
        }),
      });
    });

    await page.route("**/api/combos", async (route) => {
      const method = route.request().method();
      if (method === "GET") {
        await route.fulfill({
          status: 200,
          contentType: "application/json",
          body: JSON.stringify({ combos: state.combos }),
        });
        return;
      }

      if (method === "POST") {
        const payloadRaw = route.request().postDataJSON();
        const payload =
          payloadRaw && typeof payloadRaw === "object" ? (payloadRaw as ComboCreatePayload) : {};
        const comboId = `combo-${state.nextId++}`;
        const createdCombo = {
          id: comboId,
          name: payload.name || comboId,
          strategy: payload.strategy || "priority",
          models: payload.models || [],
          config: payload.config || {},
          isActive: true,
        };
        state.combos.push(createdCombo);

        await route.fulfill({
          status: 200,
          contentType: "application/json",
          body: JSON.stringify({ combo: createdCombo }),
        });
        return;
      }

      await route.fulfill({
        status: 405,
        contentType: "application/json",
        body: JSON.stringify({ error: "Method not allowed in test stub" }),
      });
    });

    await gotoDashboardRoute(page, "/dashboard/combos", {
      waitUntil: "domcontentloaded",
    });
    await expect(
      page.getByRole("button", { name: /create combo|criar combo/i }).first()
    ).toBeVisible();

    await page
      .getByRole("button", { name: /create combo|criar combo/i })
      .first()
      .click();

    const comboDialog = page.getByRole("dialog").first();
    await expect(comboDialog).toBeVisible();
    const comboNextButton = comboDialog.locator('[data-testid="combo-builder-next"]');
    await expect(comboNextButton).toBeDisabled();

    await comboDialog.locator('[data-testid="combo-template-high-availability"]').click();
    await expect(comboNextButton).toBeEnabled();
    await comboNextButton.click();

    await expect(comboDialog.locator('[data-testid="combo-builder-stage-steps"]')).toBeVisible();
    await expect(comboNextButton).toBeDisabled();
    await comboDialog.locator('[data-testid="combo-builder-provider"]').selectOption("openai");
    await comboDialog.locator('[data-testid="combo-builder-model"]').selectOption("qa-test-model");
    await comboDialog.locator('[data-testid="combo-builder-account"]').selectOption("conn-openai");
    await comboDialog.locator('[data-testid="combo-builder-add-step"]').click();
    await expect(comboNextButton).toBeEnabled();
    await comboNextButton.click();

    const applyRecommendationsButton = comboDialog
      .getByRole("button", { name: /apply recommendations|aplicar recomendações/i })
      .first();

    await expect(applyRecommendationsButton).toBeVisible();
    await comboDialog.locator('[data-testid="strategy-option-weighted"]').click();
    await expect(comboDialog.locator('[data-testid="strategy-change-nudge"]')).toBeVisible();
    await comboDialog.locator('[data-testid="strategy-option-priority"]').click();
    await expect(comboDialog.locator('[data-testid="strategy-change-nudge"]')).toBeVisible();
    await applyRecommendationsButton.click();

    await comboNextButton.click();

    const comboCreateButton = comboDialog
      .getByRole("button", { name: /create combo|criar combo/i })
      .last();
    const readinessPanel = comboDialog.locator('[data-testid="combo-readiness-panel"]');
    const saveBlockers = comboDialog.locator('[data-testid="combo-save-blockers"]');

    await expect(readinessPanel).toBeVisible();
    await expect(saveBlockers).toHaveCount(0);
    await expect(comboCreateButton).toBeEnabled();

    await comboCreateButton.click();
    await expect(comboDialog).toBeHidden();

    const quickTestButton = page.getByRole("button", { name: /test now|testar agora/i });
    await expect(quickTestButton).toBeVisible();
    await quickTestButton.click();

    await expect
      .poll(() => state.comboTestRequests, {
        message: "Expected the quick test CTA to hit /api/combos/test once",
      })
      .toBe(1);

    const testResultsModal = page.getByRole("dialog").last();
    await expect(testResultsModal).toContainText(/qa-test-model/i);
  });

  test("expert mode shows a single-page combo form with manual model entry", async ({ page }) => {
    const state: {
      combos: ComboStub[];
      nextId: number;
      lastPayload: ComboCreatePayload | null;
    } = {
      combos: [],
      nextId: 1,
      lastPayload: null,
    };

    await page.route("**/api/combos/metrics", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ metrics: {} }),
      });
    });

    await page.route(/\/api\/settings$/, async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ comboConfigMode: "expert" }),
      });
    });

    await page.route("**/api/settings/proxy", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ combos: {} }),
      });
    });

    await page.route("**/api/providers", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({
          connections: [
            { id: "conn-codex", provider: "codex", testStatus: "active" },
            { id: "conn-openrouter", provider: "openrouter", testStatus: "active" },
          ],
        }),
      });
    });

    await page.route("**/api/provider-nodes", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ nodes: [] }),
      });
    });

    await page.route("**/api/models/alias", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ aliases: {} }),
      });
    });

    await page.route("**/api/pricing", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({}),
      });
    });

    await page.route("**/api/combos/builder/options", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({
          providers: [
            {
              providerId: "codex",
              alias: "cx",
              displayName: "Codex",
              connectionCount: 1,
              models: [],
              connections: [
                {
                  id: "conn-codex",
                  label: "Codex Primary",
                  status: "active",
                  priority: 1,
                },
              ],
            },
            {
              providerId: "openrouter",
              alias: "openrouter",
              displayName: "OpenRouter",
              connectionCount: 1,
              models: [],
              connections: [
                {
                  id: "conn-openrouter",
                  label: "OpenRouter Primary",
                  status: "active",
                  priority: 1,
                },
              ],
            },
          ],
          comboRefs: [],
        }),
      });
    });

    await page.route("**/api/combos", async (route) => {
      const method = route.request().method();
      if (method === "GET") {
        await route.fulfill({
          status: 200,
          contentType: "application/json",
          body: JSON.stringify({ combos: state.combos }),
        });
        return;
      }

      if (method === "POST") {
        const payloadRaw = route.request().postDataJSON();
        const payload =
          payloadRaw && typeof payloadRaw === "object" ? (payloadRaw as ComboCreatePayload) : {};
        state.lastPayload = payload;
        const comboId = `combo-${state.nextId++}`;
        const createdCombo = {
          id: comboId,
          name: payload.name || comboId,
          strategy: payload.strategy || "priority",
          models: payload.models || [],
          config: payload.config || {},
          isActive: true,
        };
        state.combos.push(createdCombo);

        await route.fulfill({
          status: 200,
          contentType: "application/json",
          body: JSON.stringify({ combo: createdCombo }),
        });
        return;
      }

      await route.fulfill({ status: 405, body: "Method not allowed in test stub" });
    });

    await gotoDashboardRoute(page, "/dashboard/combos", {
      waitUntil: "domcontentloaded",
    });
    await page
      .getByRole("button", { name: /create combo|criar combo/i })
      .first()
      .click();

    const comboDialog = page.getByRole("dialog").first();
    await expect(comboDialog).toBeVisible();
    await expect(comboDialog.locator('[data-testid="combo-builder-next"]')).toHaveCount(0);
    await expect(comboDialog.locator('[data-testid="combo-builder-stage-steps"]')).toHaveCount(0);
    await expect(comboDialog.locator('[data-testid="combo-readiness-panel"]')).toBeVisible();
    await expect(comboDialog.locator('[data-testid="combo-browse-catalog"]')).toBeVisible();
    await comboDialog.locator('[data-testid="combo-browse-catalog"]').click();
    const modelCatalogDialog = page.getByRole("dialog", {
      name: /add model to combo|adicionar modelo ao combo/i,
    });
    await expect(modelCatalogDialog).toBeVisible();
    await modelCatalogDialog.getByRole("button", { name: /close/i }).click();
    await expect(comboDialog.getByText(/recommended setup|how to use this strategy/i)).toHaveCount(
      0
    );

    await comboDialog.locator('[data-testid="combo-name-input"]').fill("expert-stack");
    await comboDialog.locator('[data-testid="combo-manual-model-input"]').fill("cx/gpt-5.5");
    await comboDialog.locator('[data-testid="combo-manual-model-add"]').click();
    await comboDialog
      .locator('[data-testid="combo-manual-model-input"]')
      .fill("openrouter/openai/gpt-5.5");
    await comboDialog.locator('[data-testid="combo-manual-model-add"]').click();
    await expect(comboDialog.locator('[data-testid="combo-readiness-panel"]')).toHaveCount(0);

    // New advanced settings: failoverBeforeRetry, maxSetRetries, setRetryDelayMs
    await comboDialog.locator("#failoverBeforeRetry").check();
    // maxSetRetries: placeholder "0" is unique (maxRetries uses "1")
    await comboDialog.locator('input[placeholder="0"]').fill("2");
    // setRetryDelayMs: placeholder "2000" — last occurrence is the new field
    await comboDialog.locator('input[placeholder="2000"]').last().fill("1500");

    await comboDialog
      .getByRole("button", { name: /create combo|criar combo/i })
      .last()
      .click();
    await expect(comboDialog).toBeHidden();

    expect(state.lastPayload?.models).toEqual([
      {
        kind: "model",
        providerId: "codex",
        model: "codex/gpt-5.5",
        weight: 0,
      },
      {
        kind: "model",
        providerId: "openrouter",
        model: "openrouter/openai/gpt-5.5",
        weight: 0,
      },
    ]);
    expect(state.lastPayload?.config?.failoverBeforeRetry).toBe(true);
    expect(state.lastPayload?.config?.maxSetRetries).toBe(2);
    expect(state.lastPayload?.config?.setRetryDelayMs).toBe(1500);
  });

  test("allows dragging combo cards to persist manual order", async ({ page }) => {
    const state: {
      combos: ComboStub[];
      reorderRequests: number;
    } = {
      combos: [
        {
          id: "combo-1",
          name: "alpha-combo",
          strategy: "priority",
          models: ["openai/alpha"],
          config: {},
          isActive: true,
          sortOrder: 1,
        },
        {
          id: "combo-2",
          name: "bravo-combo",
          strategy: "priority",
          models: ["openai/bravo"],
          config: {},
          isActive: true,
          sortOrder: 2,
        },
        {
          id: "combo-3",
          name: "charlie-combo",
          strategy: "priority",
          models: ["openai/charlie"],
          config: {},
          isActive: true,
          sortOrder: 3,
        },
      ],
      reorderRequests: 0,
    };

    await page.route("**/api/combos/metrics", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ metrics: {} }),
      });
    });

    await page.route(/\/api\/settings$/, async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ comboConfigMode: "guided" }),
      });
    });

    await page.route("**/api/settings/proxy", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ combos: {} }),
      });
    });

    await page.route("**/api/providers", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ connections: [] }),
      });
    });

    await page.route("**/api/provider-nodes", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ nodes: [] }),
      });
    });

    await page.route("**/api/combos/reorder", async (route) => {
      state.reorderRequests += 1;
      const payload = route.request().postDataJSON() as { comboIds?: string[] };
      const nextIds = Array.isArray(payload?.comboIds) ? payload.comboIds : [];
      const comboById = new Map(state.combos.map((combo) => [combo.id, combo]));
      state.combos = nextIds
        .map((id, index) => {
          const combo = comboById.get(id);
          return combo ? { ...combo, sortOrder: index + 1 } : null;
        })
        .filter((combo): combo is ComboStub => combo !== null);

      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ combos: state.combos }),
      });
    });

    await page.route("**/api/combos", async (route) => {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ combos: state.combos }),
      });
    });

    await gotoDashboardRoute(page, "/dashboard/combos", {
      waitUntil: "domcontentloaded",
    });
    await expect(page.getByTestId("combo-card-combo-1")).toBeVisible();

    const comboCards = page.locator('[data-testid^="combo-card-"]');
    await expect
      .poll(async () =>
        comboCards.evaluateAll((nodes) => nodes.map((node) => node.getAttribute("data-testid")))
      )
      .toEqual(["combo-card-combo-1", "combo-card-combo-2", "combo-card-combo-3"]);

    await dispatchHtml5DragAndDrop(
      page,
      page.getByTestId("combo-drag-handle-combo-3"),
      page.getByTestId("combo-card-combo-1")
    );

    await expect.poll(() => state.reorderRequests).toBe(1);
    await expect
      .poll(async () =>
        comboCards.evaluateAll((nodes) => nodes.map((node) => node.getAttribute("data-testid")))
      )
      .toEqual(["combo-card-combo-3", "combo-card-combo-1", "combo-card-combo-2"]);

    await page.reload({ waitUntil: "domcontentloaded" });
    await expect(page.getByTestId("combo-card-combo-3")).toBeVisible();

    await expect
      .poll(async () =>
        comboCards.evaluateAll((nodes) => nodes.map((node) => node.getAttribute("data-testid")))
      )
      .toEqual(["combo-card-combo-3", "combo-card-combo-1", "combo-card-combo-2"]);
  });
});
