/**
 * 03 – Backup configuration tests.
 *
 * Exercises the pre-sync backup system by toggling config flags through
 * the app API and triggering re-syncs on repos that were already mirrored
 * by the 02-mirror-workflow suite.
 *
 * What is tested:
 *   B1. Enable backupStrategy: "always" in config
 *   B2. Confirm mirrored repos exist in Gitea (precondition)
 *   B3. Trigger a re-sync with backup enabled — verify the backup code path
 *       runs (snapshot activity entries appear in the activity log)
 *   B4. Inspect activity log for snapshot-related entries
 *   B5. Enable blockSyncOnBackupFailure and verify the flag is persisted
 *   B6. Disable backup (backupStrategy: "disabled") and verify config resets cleanly
 */

import { test, expect } from "@playwright/test";
import {
  APP_URL,
  GITEA_URL,
  GITEA_MIRROR_ORG,
  GiteaAPI,
  getAppSessionCookies,
  saveConfig,
  getRepositoryIds,
  triggerSyncRepo,
} from "./helpers";

test.describe("E2E: Backup configuration", () => {
  let giteaApi: GiteaAPI;
  let appCookies = "";

  test.beforeAll(async () => {
    giteaApi = new GiteaAPI(GITEA_URL);
    try {
      await giteaApi.createToken();
    } catch {
      console.log(
        "[Backup] Could not create Gitea token; tests may be limited",
      );
    }
  });

  test.afterAll(async () => {
    await giteaApi.dispose();
  });

  // ── B1 ─────────────────────────────────────────────────────────────────────

  test("Step B1: Enable backup in config", async ({ request }) => {
    appCookies = await getAppSessionCookies(request);

    const giteaToken = giteaApi.getTokenValue();
    expect(giteaToken, "Gitea token required").toBeTruthy();

    // Save config with backup strategy set to "always"
    await saveConfig(request, giteaToken, appCookies, {
      giteaConfig: {
        backupStrategy: "always",
        blockSyncOnBackupFailure: false,
        backupRetentionCount: 5,
        backupDirectory: "data/repo-backups",
      },
    });

    // Verify config was saved
    const configResp = await request.get(`${APP_URL}/api/config`, {
      headers: { Cookie: appCookies },
      failOnStatusCode: false,
    });
    expect(configResp.status()).toBeLessThan(500);

    if (configResp.ok()) {
      const configData = await configResp.json();
      const giteaCfg = configData.giteaConfig ?? configData.gitea ?? {};
      console.log(
        `[Backup] Config saved: backupStrategy=${giteaCfg.backupStrategy}, blockOnFailure=${giteaCfg.blockSyncOnBackupFailure}`,
      );
    }
  });

  // ── B2 ─────────────────────────────────────────────────────────────────────

  test("Step B2: Verify mirrored repos exist in Gitea before backup test", async () => {
    // We need repos to already be mirrored from the 02-mirror-workflow suite
    const orgRepos = await giteaApi.listOrgRepos(GITEA_MIRROR_ORG);
    console.log(
      `[Backup] Repos in ${GITEA_MIRROR_ORG}: ${orgRepos.length} (${orgRepos.map((r: any) => r.name).join(", ")})`,
    );

    if (orgRepos.length === 0) {
      console.log(
        "[Backup] WARNING: No repos in Gitea yet. Backup test will verify " +
          "job creation but not bundle creation.",
      );
    }
  });

  // ── B3 ─────────────────────────────────────────────────────────────────────

  test("Step B3: Trigger re-sync with backup enabled", async ({ request }) => {
    if (!appCookies) {
      appCookies = await getAppSessionCookies(request);
    }

    // Fetch mirrored repository IDs (sync-repo requires them)
    const { ids: repositoryIds, repos } = await getRepositoryIds(
      request,
      appCookies,
      { status: "mirrored" },
    );

    // Also include repos with "success" status
    if (repositoryIds.length === 0) {
      const { ids: successIds } = await getRepositoryIds(
        request,
        appCookies,
        { status: "success" },
      );
      repositoryIds.push(...successIds);
    }

    // Fall back to all repos if no mirrored/success repos
    if (repositoryIds.length === 0) {
      const { ids: allIds } = await getRepositoryIds(request, appCookies);
      repositoryIds.push(...allIds);
    }

    console.log(
      `[Backup] Found ${repositoryIds.length} repos to re-sync: ` +
        repos.map((r: any) => r.name).join(", "),
    );

    expect(
      repositoryIds.length,
      "Need at least one repo to test backup",
    ).toBeGreaterThan(0);

    // Trigger sync-repo — this calls syncGiteaRepoEnhanced which checks
    // shouldCreatePreSyncBackup and creates bundles before syncing
    const status = await triggerSyncRepo(
      request,
      appCookies,
      repositoryIds,
      25_000,
    );
    console.log(`[Backup] Sync-repo response: ${status}`);
    expect(status, "Sync-repo should accept request").toBeLessThan(500);
  });

  // ── B4 ─────────────────────────────────────────────────────────────────────

  test("Step B4: Verify backup-related activity in logs", async ({
    request,
  }) => {
    if (!appCookies) {
      appCookies = await getAppSessionCookies(request);
    }

    const activitiesResp = await request.get(`${APP_URL}/api/activities`, {
      headers: { Cookie: appCookies },
      failOnStatusCode: false,
    });

    if (!activitiesResp.ok()) {
      console.log(
        `[Backup] Could not fetch activities: ${activitiesResp.status()}`,
      );
      return;
    }

    const activities = await activitiesResp.json();
    const jobs: any[] = Array.isArray(activities)
      ? activities
      : (activities.jobs ?? activities.activities ?? []);

    // Look for backup / snapshot related messages
    const backupJobs = jobs.filter(
      (j: any) =>
        j.message?.toLowerCase().includes("snapshot") ||
        j.message?.toLowerCase().includes("backup") ||
        j.details?.toLowerCase().includes("snapshot") ||
        j.details?.toLowerCase().includes("backup") ||
        j.details?.toLowerCase().includes("bundle"),
    );

    console.log(
      `[Backup] Backup-related activity entries: ${backupJobs.length}`,
    );
    for (const j of backupJobs.slice(0, 10)) {
      console.log(
        `[Backup]   • ${j.repositoryName ?? "?"}: ${j.status} — ${j.message ?? ""} | ${(j.details ?? "").substring(0, 120)}`,
      );
    }

    // We expect at least some backup-related entries if repos were mirrored
    const orgRepos = await giteaApi.listOrgRepos(GITEA_MIRROR_ORG);
    if (orgRepos.length > 0) {
      // With repos in Gitea, the backup system should have tried to create
      // snapshots. All snapshots should succeed.
      expect(
        backupJobs.length,
        "Expected at least one backup/snapshot activity entry when " +
          "backupStrategy is 'always' and repos exist in Gitea",
      ).toBeGreaterThan(0);

      // Check for any failed backups
      const failedBackups = backupJobs.filter(
        (j: any) =>
          j.status === "failed" &&
          (j.message?.toLowerCase().includes("snapshot") ||
            j.details?.toLowerCase().includes("snapshot")),
      );
      expect(
        failedBackups.length,
        `Expected all backups to succeed, but ${failedBackups.length} backup(s) failed. ` +
          `Failed: ${failedBackups.map((j: any) => `${j.repositoryName}: ${j.details?.substring(0, 100)}`).join("; ")}`,
      ).toBe(0);

      console.log(
        `[Backup] Confirmed: backup system was invoked for ${backupJobs.length} repos`,
      );
    }

    // Dump all recent jobs for debugging visibility
    console.log(`[Backup] All recent jobs (last 20):`);
    for (const j of jobs.slice(0, 20)) {
      console.log(
        `[Backup]   - [${j.status}] ${j.repositoryName ?? "?"}: ${j.message ?? ""} ` +
          `${j.details ? `(${j.details.substring(0, 80)})` : ""}`,
      );
    }
  });

  // ── B5 ─────────────────────────────────────────────────────────────────────

  test("Step B5: Enable blockSyncOnBackupFailure and verify behavior", async ({
    request,
  }) => {
    if (!appCookies) {
      appCookies = await getAppSessionCookies(request);
    }

    const giteaToken = giteaApi.getTokenValue();

    // Update config to block sync on backup failure
    await saveConfig(request, giteaToken, appCookies, {
      giteaConfig: {
        backupStrategy: "always",
        blockSyncOnBackupFailure: true,
        backupRetentionCount: 5,
        backupDirectory: "data/repo-backups",
      },
    });
    console.log("[Backup] Config updated: blockSyncOnBackupFailure=true");

    // Verify the flag persisted
    const configResp = await request.get(`${APP_URL}/api/config`, {
      headers: { Cookie: appCookies },
      failOnStatusCode: false,
    });
    if (configResp.ok()) {
      const configData = await configResp.json();
      const giteaCfg = configData.giteaConfig ?? configData.gitea ?? {};
      expect(giteaCfg.blockSyncOnBackupFailure).toBe(true);
      console.log(
        `[Backup] Verified: blockSyncOnBackupFailure=${giteaCfg.blockSyncOnBackupFailure}`,
      );
    }
  });

  // ── B6 ─────────────────────────────────────────────────────────────────────

  test("Step B6: Disable backup and verify config resets", async ({
    request,
  }) => {
    if (!appCookies) {
      appCookies = await getAppSessionCookies(request);
    }

    const giteaToken = giteaApi.getTokenValue();

    // Disable backup
    await saveConfig(request, giteaToken, appCookies, {
      giteaConfig: {
        backupStrategy: "disabled",
        blockSyncOnBackupFailure: false,
      },
    });

    const configResp = await request.get(`${APP_URL}/api/config`, {
      headers: { Cookie: appCookies },
      failOnStatusCode: false,
    });
    if (configResp.ok()) {
      const configData = await configResp.json();
      const giteaCfg = configData.giteaConfig ?? configData.gitea ?? {};
      console.log(
        `[Backup] After disable: backupStrategy=${giteaCfg.backupStrategy}`,
      );
    }
    console.log("[Backup] Backup configuration test complete");
  });
});
