/**
 * Integration tests for `wmill job` commands:
 * list, get, result, logs, cancel
 */

import { expect, test, describe } from "bun:test";
import { withTestBackend } from "./test_backend.ts";
import {
  setupWorkspaceProfile,
  createRemoteScript,
  createRemoteFlow,
  createRemoteMultiStepFlow,
  createRemoteFailingFlow,
  runRemoteScript,
  runRemoteFlow,
  waitForJob,
} from "./new_commands_helpers.ts";

describe("job command", () => {
  test("job list returns valid JSON", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const scriptPath = `f/test/job_test_${uniqueId}`;
      await createRemoteScript(backend, scriptPath);
      const jobId = await runRemoteScript(backend, scriptPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "list", "--json"],
        tempDir
      );

      expect(result.code).toEqual(0);
      const parsed = JSON.parse(result.stdout);
      expect(Array.isArray(parsed)).toBe(true);
      expect(parsed.some((j: any) => j.id === jobId)).toBe(true);
    });
  });

  test("job list shows table output", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const scriptPath = `f/test/job_table_${uniqueId}`;
      await createRemoteScript(backend, scriptPath);
      const jobId = await runRemoteScript(backend, scriptPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "list"],
        tempDir
      );

      expect(result.code).toEqual(0);
      expect(result.stdout).toContain("ID");
      expect(result.stdout).toContain("Status");
      expect(result.stdout).toContain(jobId);
    });
  });

  test("job list --script-path filters correctly", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const scriptPath = `f/test/filter_test_${uniqueId}`;
      await createRemoteScript(backend, scriptPath);
      const jobId = await runRemoteScript(backend, scriptPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "list", "--json", "--script-path", scriptPath],
        tempDir
      );

      expect(result.code).toEqual(0);
      const parsed = JSON.parse(result.stdout);
      expect(parsed.every((j: any) => j.script_path === scriptPath)).toBe(true);
    });
  });

  test("job get returns job details", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const scriptPath = `f/test/job_get_${uniqueId}`;
      await createRemoteScript(backend, scriptPath);
      const jobId = await runRemoteScript(backend, scriptPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "get", jobId],
        tempDir
      );

      expect(result.code).toEqual(0);
      expect(result.stdout).toContain("ID:");
      expect(result.stdout).toContain(jobId);
      expect(result.stdout).toContain("Status:");
      expect(result.stdout).toContain("success");
    });
  });

  test("job get --json returns valid JSON", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const scriptPath = `f/test/job_get_json_${uniqueId}`;
      await createRemoteScript(backend, scriptPath);
      const jobId = await runRemoteScript(backend, scriptPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "get", jobId, "--json"],
        tempDir
      );

      expect(result.code).toEqual(0);
      const parsed = JSON.parse(result.stdout);
      expect(parsed.id).toBe(jobId);
      expect(parsed.success).toBe(true);
    });
  });

  test("job result returns job result as JSON", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const scriptPath = `f/test/job_result_${uniqueId}`;
      await createRemoteScript(backend, scriptPath);
      const jobId = await runRemoteScript(backend, scriptPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "result", jobId],
        tempDir
      );

      expect(result.code).toEqual(0);
      // Result may be in stdout or combined output
      const output = result.stdout.trim();
      expect(output.length).toBeGreaterThan(0);
      const parsed = JSON.parse(output);
      expect(parsed).toBe("hello");
    });
  });

  test("job logs returns job logs", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const scriptPath = `f/test/job_logs_${uniqueId}`;
      await createRemoteScript(backend, scriptPath);
      const jobId = await runRemoteScript(backend, scriptPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "logs", jobId],
        tempDir
      );

      expect(result.code).toEqual(0);
      expect(result.stdout.length).toBeGreaterThan(0);
    });
  });

  test("job logs for flow job aggregates step logs", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const flowPath = `f/test/flow_logs_${uniqueId}`;
      await createRemoteMultiStepFlow(backend, flowPath);
      const jobId = await runRemoteFlow(backend, flowPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "logs", jobId],
        tempDir
      );

      expect(result.code).toEqual(0);
      // Should show labeled step headers instead of "no direct logs"
      expect(result.stdout).toContain("======");
      expect(result.stdout).toContain("a: Generate data");
    });
  });

  test("default action (wmill job) lists jobs", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const result = await backend.runCLICommand(
        ["job", "--json"],
        tempDir
      );

      expect(result.code).toEqual(0);
      const parsed = JSON.parse(result.stdout);
      expect(Array.isArray(parsed)).toBe(true);
    });
  });

  test("job --help shows all subcommands", async () => {
    await withTestBackend(async (backend, tempDir) => {
      const result = await backend.runCLICommand(["job", "--help"], tempDir);
      const output = result.stdout + result.stderr;
      expect(output).toContain("list");
      expect(output).toContain("get");
      expect(output).toContain("result");
      expect(output).toContain("logs");
      expect(output).toContain("cancel");
      expect(output).toContain("--failed");
      expect(output).toContain("--running");
      expect(output).toContain("--parent");
      expect(output).toContain("--is-flow-step");
    });
  });

  test("job get for flow shows hierarchical step tree", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const flowPath = `f/test/flow_get_${uniqueId}`;
      await createRemoteMultiStepFlow(backend, flowPath);
      const jobId = await runRemoteFlow(backend, flowPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "get", jobId],
        tempDir
      );

      expect(result.code).toEqual(0);
      expect(result.stdout).toContain("Steps:");
      // Should show step IDs from the flow definition
      expect(result.stdout).toContain("a");
      expect(result.stdout).toContain("b");
      // Should show status icons (✓ for success)
      expect(result.stdout).toContain("✓");
    });
  });

  test("job get --json for flow includes flow_status", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const flowPath = `f/test/flow_json_${uniqueId}`;
      await createRemoteMultiStepFlow(backend, flowPath);
      const jobId = await runRemoteFlow(backend, flowPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "get", jobId, "--json"],
        tempDir
      );

      expect(result.code).toEqual(0);
      const parsed = JSON.parse(result.stdout);
      expect(parsed.flow_status).toBeDefined();
      expect(parsed.flow_status.modules).toBeDefined();
      expect(parsed.flow_status.modules.length).toBe(2);
    });
  });

  test("job list --parent shows sub-jobs of a flow", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const flowPath = `f/test/flow_parent_${uniqueId}`;
      await createRemoteMultiStepFlow(backend, flowPath);
      const jobId = await runRemoteFlow(backend, flowPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "list", "--json", "--parent", jobId],
        tempDir
      );

      expect(result.code).toEqual(0);
      const parsed = JSON.parse(result.stdout);
      expect(Array.isArray(parsed)).toBe(true);
      // A 2-step flow should have at least 2 sub-jobs
      expect(parsed.length).toBeGreaterThanOrEqual(2);
      // All sub-jobs should reference the parent flow
      expect(parsed.every((j: any) => j.parent_job === jobId)).toBe(true);
    });
  });

  test("job list --all includes sub-jobs", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const flowPath = `f/test/flow_all_${uniqueId}`;
      await createRemoteMultiStepFlow(backend, flowPath);
      const jobId = await runRemoteFlow(backend, flowPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "list", "--json", "--all"],
        tempDir
      );

      expect(result.code).toEqual(0);
      const parsed = JSON.parse(result.stdout);
      // Should contain both the parent flow and its sub-jobs
      const parentJob = parsed.find((j: any) => j.id === jobId);
      const subJobs = parsed.filter((j: any) => j.parent_job === jobId);
      expect(parentJob).toBeDefined();
      expect(subJobs.length).toBeGreaterThanOrEqual(2);
    });
  });

  test("job get for failed flow shows failure status", async () => {
    await withTestBackend(async (backend, tempDir) => {
      await setupWorkspaceProfile(backend);

      const uniqueId = Date.now();
      const flowPath = `f/test/flow_fail_${uniqueId}`;
      await createRemoteFailingFlow(backend, flowPath);
      const jobId = await runRemoteFlow(backend, flowPath);
      await waitForJob(backend, jobId);

      const result = await backend.runCLICommand(
        ["job", "get", jobId],
        tempDir
      );

      expect(result.code).toEqual(0);
      expect(result.stdout).toContain("failure");
      expect(result.stdout).toContain("Steps:");
      // Step a should succeed, step b should fail
      expect(result.stdout).toContain("✓");
      expect(result.stdout).toContain("✗");
    });
  });
});
