import { beforeEach, describe, expect, it, vi } from "vitest";
import { join, resolve } from "path";
import type { ParsedArgs } from "../src/cli/args";
import { runCanvas } from "../src/cli/commands/canvas";
import { runInspiredesignCommand } from "../src/cli/commands/inspiredesign";
import { runMacroResolve } from "../src/cli/commands/macro-resolve";
import { runProductVideoCommand } from "../src/cli/commands/product-video";
import { runResearchCommand } from "../src/cli/commands/research";
import { runShoppingCommand } from "../src/cli/commands/shopping";
import { DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS } from "../src/cli/transport-timeouts";
import {
  buildInspiredesignFollowthroughSummary,
  buildInspiredesignNextStep
} from "../src/inspiredesign/handoff";

const { callDaemon } = vi.hoisted(() => ({
  callDaemon: vi.fn()
}));

vi.mock("../src/cli/client", () => ({
  callDaemon
}));

const makeArgs = (command: ParsedArgs["command"], rawArgs: string[]): ParsedArgs => ({
  command,
  mode: undefined,
  withConfig: false,
  noPrompt: false,
  noInteractive: false,
  quiet: false,
  outputFormat: "json",
  transport: "relay",
  skillsMode: "global",
  fullInstall: false,
  rawArgs
});

const defaultWorkflowOutputDir = resolve(".opendevbrowser");

describe("workflow CLI commands", () => {
  beforeEach(() => {
    callDaemon.mockReset();
  });

  it("returns Canvas pre-session repair guidance without daemon dispatch", async () => {
    const result = await runCanvas(makeArgs("canvas", [
      "--command",
      "canvas.plan.set",
      "--params",
      "{}"
    ]));

    expect(result).toMatchObject({
      success: false,
      message: expect.stringContaining("Missing canvasSessionId"),
      exitCode: 2,
      data: {
        command: "canvas.plan.set",
        result: expect.objectContaining({
          code: "missing_canvas_session_id",
          nextStepGuidance: expect.objectContaining({
            workflow: "canvas",
            reasonCode: "missing_canvas_session_id"
          }),
          guidance: expect.objectContaining({
            recommendedNextCommands: ["canvas.session.open", "canvas.plan.set"],
            reason: expect.stringContaining("Missing canvasSessionId"),
            nextStepGuidance: expect.objectContaining({
              workflow: "canvas",
              reasonCode: "missing_canvas_session_id"
            }),
            paramsExamples: expect.arrayContaining([
              expect.objectContaining({ command: "canvas.plan.set" })
            ])
          }),
          paramsExamples: expect.arrayContaining([
            expect.objectContaining({ command: "canvas.plan.set" })
          ])
        })
      }
    });
  });

  it("parses and dispatches research run payload", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    const result = await runResearchCommand(makeArgs("research", [
      "run",
      "--topic=browser automation",
      "--days=14",
      "--source-selection=all",
      "--sources=web,shopping",
      "--mode=context",
      "--include-engagement",
      "--limit-per-source=5",
      "--output-dir=/tmp/out",
      "--ttl-hours=72"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("research.run", {
      topic: "browser automation",
      days: 14,
      from: undefined,
      to: undefined,
      sourceSelection: "all",
      sources: ["web", "shopping"],
      mode: "context",
      includeEngagement: true,
      limitPerSource: 5,
      timeoutMs: DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS,
      outputDir: "/tmp/out",
      ttlHours: 72,
      useCookies: undefined,
      cookiePolicyOverride: undefined
    });
    expect(result).toMatchObject({ success: true });
  });

  it("supports explicit timeout for research workflows", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runResearchCommand(makeArgs("research", [
      "run",
      "--topic=browser automation",
      "--timeout-ms=45000"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("research.run", {
      topic: "browser automation",
      days: undefined,
      from: undefined,
      to: undefined,
      sourceSelection: undefined,
      sources: undefined,
      mode: "compact",
      includeEngagement: false,
      limitPerSource: undefined,
      timeoutMs: 45000,
      outputDir: defaultWorkflowOutputDir,
      ttlHours: undefined,
      useCookies: undefined,
      cookiePolicyOverride: undefined
    });
  });

  it("resolves relative workflow output directories before daemon dispatch", async () => {
    callDaemon.mockResolvedValue({ ok: true });
    const outputDir = "workflow-artifacts";

    await runResearchCommand(makeArgs("research", ["run", "--topic=browser automation", `--output-dir=${outputDir}`]));
    expect(callDaemon).toHaveBeenLastCalledWith("research.run", expect.objectContaining({
      outputDir: resolve(outputDir)
    }));

    await runShoppingCommand(makeArgs("shopping", ["run", "--query=usb hub", `--output-dir=${outputDir}`]));
    expect(callDaemon).toHaveBeenLastCalledWith("shopping.run", expect.objectContaining({
      outputDir: resolve(outputDir)
    }), {
      timeoutMs: 180000
    });

    await runInspiredesignCommand(makeArgs("inspiredesign", ["run", "--brief=Design system", `--output-dir=${outputDir}`]));
    expect(callDaemon).toHaveBeenLastCalledWith("inspiredesign.run", expect.objectContaining({
      outputDir: resolve(outputDir)
    }));

    await runProductVideoCommand(makeArgs("product-video", ["run", "--product-name=Sample Product", `--output-dir=${outputDir}`]));
    expect(callDaemon).toHaveBeenLastCalledWith("product.video.run", expect.objectContaining({
      output_dir: resolve(outputDir)
    }));
  });

  it("rejects blank workflow output directories", async () => {
    await expect(runResearchCommand(makeArgs("research", ["run", "--topic=browser automation", "--output-dir="]))).rejects.toThrow("Missing value for --output-dir");
    await expect(runResearchCommand(makeArgs("research", ["run", "--topic=browser automation", "--output-dir", "   "]))).rejects.toThrow("Missing value for --output-dir");
    await expect(runShoppingCommand(makeArgs("shopping", ["run", "--query=usb hub", "--output-dir="]))).rejects.toThrow("Missing value for --output-dir");
    await expect(runShoppingCommand(makeArgs("shopping", ["run", "--query=usb hub", "--output-dir", "   "]))).rejects.toThrow("Missing value for --output-dir");
    await expect(runInspiredesignCommand(makeArgs("inspiredesign", ["run", "--brief=Design system", "--output-dir="]))).rejects.toThrow("Missing value for --output-dir");
    await expect(runInspiredesignCommand(makeArgs("inspiredesign", ["run", "--brief=Design system", "--output-dir", "   "]))).rejects.toThrow("Missing value for --output-dir");
    await expect(runProductVideoCommand(makeArgs("product-video", ["run", "--product-name=Sample Product", "--output-dir="]))).rejects.toThrow("Missing value for --output-dir");
    await expect(runProductVideoCommand(makeArgs("product-video", ["run", "--product-name=Sample Product", "--output-dir", "   "]))).rejects.toThrow("Missing value for --output-dir");
  });

  it("parses and dispatches shopping run payload", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runShoppingCommand(makeArgs("shopping", [
      "run",
      "--query=usb hub",
      "--providers=shopping/amazon,shopping/others",
      "--budget=50",
      "--region=us",
      "--sort=lowest_price",
      "--mode=md"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("shopping.run", {
      query: "usb hub",
      providers: ["shopping/amazon", "shopping/others"],
      budget: 50,
      region: "us",
      sort: "lowest_price",
      mode: "md",
      timeoutMs: DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS,
      outputDir: defaultWorkflowOutputDir,
      ttlHours: undefined,
      browserMode: undefined,
      useCookies: undefined,
      cookiePolicyOverride: undefined
    }, {
      timeoutMs: 180000
    });
  });

  it("supports explicit timeout for shopping workflows", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runShoppingCommand(makeArgs("shopping", [
      "run",
      "--query=wireless mouse",
      "--providers=shopping/bestbuy",
      "--timeout-ms=45000"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("shopping.run", {
      query: "wireless mouse",
      providers: ["shopping/bestbuy"],
      budget: undefined,
      region: undefined,
      sort: undefined,
      mode: "compact",
      timeoutMs: 45000,
      outputDir: defaultWorkflowOutputDir,
      ttlHours: undefined,
      useCookies: undefined,
      cookiePolicyOverride: undefined
    }, {
      timeoutMs: 120000
    });
  });

  it("extends shopping transport timeout beyond the previous client cap for long-running workflows", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runShoppingCommand(makeArgs("shopping", [
      "run",
      "--query=wireless mouse",
      "--providers=shopping/bestbuy",
      "--timeout-ms=360000"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("shopping.run", {
      query: "wireless mouse",
      providers: ["shopping/bestbuy"],
      budget: undefined,
      region: undefined,
      sort: undefined,
      mode: "compact",
      timeoutMs: 360000,
      outputDir: defaultWorkflowOutputDir,
      ttlHours: undefined,
      useCookies: undefined,
      cookiePolicyOverride: undefined
    }, {
      timeoutMs: 420000
    });
  });

  it("forwards explicit shopping browser-mode overrides", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runShoppingCommand(makeArgs("shopping", [
      "run",
      "--query=macbook pro m4 32gb ram",
      "--browser-mode=extension"
    ]));
    await runShoppingCommand(makeArgs("shopping", [
      "run",
      "--query=macbook pro m4 32gb ram",
      "--browser-mode=managed"
    ]));
    await runShoppingCommand(makeArgs("shopping", [
      "run",
      "--query=macbook pro m4 32gb ram",
      "--browser-mode=auto"
    ]));

    expect(callDaemon).toHaveBeenNthCalledWith(1, "shopping.run", expect.objectContaining({
      query: "macbook pro m4 32gb ram",
      browserMode: "extension"
    }), {
      timeoutMs: 180000
    });
    expect(callDaemon).toHaveBeenNthCalledWith(2, "shopping.run", expect.objectContaining({
      query: "macbook pro m4 32gb ram",
      browserMode: "managed"
    }), {
      timeoutMs: 180000
    });
    expect(callDaemon).toHaveBeenNthCalledWith(3, "shopping.run", expect.objectContaining({
      query: "macbook pro m4 32gb ram",
      browserMode: "auto"
    }), {
      timeoutMs: 180000
    });
  });

  it("forwards explicit research browser-mode overrides", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runResearchCommand(makeArgs("research", [
      "run",
      "--topic=browser automation on X",
      "--browser-mode=extension"
    ]));
    await runResearchCommand(makeArgs("research", [
      "run",
      "--topic=browser automation on Reddit",
      "--browser-mode=managed"
    ]));
    await runResearchCommand(makeArgs("research", [
      "run",
      "--topic=browser automation",
      "--browser-mode=auto"
    ]));

    expect(callDaemon).toHaveBeenNthCalledWith(1, "research.run", expect.objectContaining({
      topic: "browser automation on X",
      browserMode: "extension"
    }));
    expect(callDaemon).toHaveBeenNthCalledWith(2, "research.run", expect.objectContaining({
      topic: "browser automation on Reddit",
      browserMode: "managed"
    }));
    expect(callDaemon).toHaveBeenNthCalledWith(3, "research.run", expect.objectContaining({
      topic: "browser automation",
      browserMode: "auto"
    }));
  });

  it("forwards explicit browser-mode overrides across provider workflows", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runProductVideoCommand(makeArgs("product-video", [
      "run",
      "--product-url=https://example.com/item",
      "--browser-mode=extension"
    ]));
    expect(callDaemon).toHaveBeenLastCalledWith("product.video.run", expect.objectContaining({
      product_url: "https://example.com/item",
      browserMode: "extension"
    }));

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "run",
      "--brief=Reference audit",
      "--browser-mode=managed"
    ]));
    expect(callDaemon).toHaveBeenLastCalledWith("inspiredesign.run", expect.objectContaining({
      brief: "Reference audit",
      browserMode: "managed"
    }));

    await runMacroResolve(makeArgs("macro-resolve", [
      "--expression=@community.search(\"openai\")",
      "--execute",
      "--browser-mode=extension",
      "--use-cookies",
      "--cookie-policy=required"
    ]));
    expect(callDaemon).toHaveBeenLastCalledWith("macro.resolve", expect.objectContaining({
      expression: "@community.search(\"openai\")",
      browserMode: "extension",
      useCookies: true,
      cookiePolicyOverride: "required"
    }));

    await runMacroResolve(makeArgs("macro-resolve", [
      "--expression=@community.search(\"openai\")",
      "--execute",
      "--browser-mode=extension",
      "--use-cookies",
      "false",
      "--cookie-policy=auto"
    ]));
    expect(callDaemon).toHaveBeenLastCalledWith("macro.resolve", expect.objectContaining({
      expression: "@community.search(\"openai\")",
      browserMode: "extension",
      useCookies: false,
      cookiePolicyOverride: "auto"
    }));
  });

  it("surfaces provider follow-up requirements in workflow completion messages", async () => {
    callDaemon.mockResolvedValue({
      meta: {
        failures: [{
          provider: "shopping/costco",
          error: {
            code: "auth",
            reasonCode: "token_required",
            details: {
              constraint: {
                kind: "session_required",
                evidenceCode: "auth_required"
              }
            }
          }
        }]
      }
    });

    const result = await runShoppingCommand(makeArgs("shopping", [
      "run",
      "--query=wireless mouse",
      "--providers=shopping/costco"
    ]));

    expect(result.message).toBe(
      "Shopping workflow completed with provider follow-up required: Costco requires login or an existing session. Next step: Reuse a user-authorized signed-in browser session, load cookies only from that authorized session, or use the provider sign-in flow."
    );
  });

  it("prefers explicit camelCase workflow summaries in completion messages", async () => {
    callDaemon.mockResolvedValue({
      meta: {
        primaryConstraintSummary: "Manual browser follow-up is required before provider resolution can continue.",
        failures: [{
          provider: "shopping/costco",
          error: {
            code: "auth",
            reasonCode: "token_required",
            details: {
              constraint: {
                kind: "session_required",
                evidenceCode: "auth_required"
              }
            }
          }
        }]
      }
    });

    const result = await runShoppingCommand(makeArgs("shopping", [
      "run",
      "--query=wireless mouse",
      "--providers=shopping/costco"
    ]));

    expect(result.message).toBe(
      "Shopping workflow completed with provider follow-up required: Manual browser follow-up is required before provider resolution can continue. Next step: Reuse a user-authorized signed-in browser session, load cookies only from that authorized session, or use the provider sign-in flow."
    );
  });

  it("appends the first explicit workflow guidance step when primary constraint guidance is present", async () => {
    callDaemon.mockResolvedValue({
      meta: {
        primaryConstraintSummary: "Manual browser follow-up is required before provider resolution can continue.",
        primaryConstraint: {
          summary: "Manual browser follow-up is required before provider resolution can continue.",
          guidance: {
            reason: "The provider still needs a rendered browser page.",
            recommendedNextCommands: [
              "Retry with browser assistance or a headed browser session.",
              "Rerun the same provider or workflow after the rendered page is ready."
            ]
          }
        }
      }
    });

    const result = await runShoppingCommand(makeArgs("shopping", [
      "run",
      "--query=wireless mouse",
      "--providers=shopping/costco"
    ]));

    expect(result.message).toBe(
      "Shopping workflow completed with provider follow-up required: Manual browser follow-up is required before provider resolution can continue. Next step: Retry with browser assistance or a headed browser session."
    );
  });

  it("supports explicit timeout for macro-resolve execution", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runMacroResolve(makeArgs("macro-resolve", [
      "--expression=@media.search(\"browser automation x\", \"x\", 5)",
      "--execute",
      "--timeout-ms=45000"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("macro.resolve", {
      expression: "@media.search(\"browser automation x\", \"x\", 5)",
      defaultProvider: undefined,
      includeCatalog: false,
      execute: true,
      timeoutMs: 45000
    }, {
      timeoutMs: 120000
    });
  });

  it("parses and dispatches inspiredesign run payload with repeated urls", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "run",
      "--brief=Build a docs landing page contract",
      "--url=https://example.com/a",
      "--url=https://example.com/b",
      "--capture-mode=deep",
      "--include-prototype-guidance",
      "--mode=md"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", {
      brief: "Build a docs landing page contract",
      harvest: false,
      query: undefined,
      providers: undefined,
      maxReferences: undefined,
      visualEvidence: "off",
      urls: ["https://example.com/a", "https://example.com/b"],
      captureMode: "deep",
      includePrototypeGuidance: true,
      mode: "md",
      timeoutMs: DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS,
      outputDir: defaultWorkflowOutputDir,
      ttlHours: undefined,
      browserMode: undefined,
      useCookies: undefined,
      challengeAutomationMode: undefined,
      cookiePolicyOverride: undefined
    });
  });

  it("parses and dispatches inspiredesign harvest payload through inspiredesign.run", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=premium docs references",
      "--provider=web/default",
      "--provider=community/reddit",
      "--max-references=4",
      "--visual-evidence=auto"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", expect.objectContaining({
      brief: "Build a docs landing page contract",
      harvest: true,
      query: "premium docs references",
      providers: ["web/default", "community/reddit"],
      maxReferences: 4,
      visualEvidence: "auto",
      captureMode: "off",
      mode: "path"
    }));
  });

  it("applies inspiredesign harvest defaults", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=premium docs references"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", expect.objectContaining({
      harvest: true,
      query: "premium docs references",
      maxReferences: 5,
      visualEvidence: "required",
      mode: "path"
    }));
  });

  it("keeps Pinterest provider alias harvest discovery out of deep capture by default", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a fashion atelier landing page",
      "--query=couture atelier visual references",
      "--provider=pinterest"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", expect.objectContaining({
      providers: ["pinterest"],
      captureMode: "off"
    }));
  });

  it("forces deep capture for explicit non-Pinterest harvest URLs even when off is requested", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--url=https://example.com/reference",
      "--capture-mode=off"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", expect.objectContaining({
      harvest: true,
      urls: ["https://example.com/reference"],
      captureMode: "deep"
    }));
  });

  it("rejects inspiredesign harvest without query or URLs", async () => {
    await expect(runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract"
    ]))).rejects.toThrow("inspiredesign harvest requires --query or --url");
  });

  it("rejects query discovery on inspiredesign run", async () => {
    await expect(runInspiredesignCommand(makeArgs("inspiredesign", [
      "run",
      "--brief=Build a docs landing page contract",
      "--query=premium docs references"
    ]))).rejects.toThrow("--query is only supported by inspiredesign harvest");
  });

  it("accepts compatible Pinterest provider URL recovery", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--provider=social/pinterest",
      "--url=https://www.pinterest.com/pin/27654985208435505/"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", expect.objectContaining({
      harvest: true,
      providers: ["social/pinterest"],
      urls: ["https://www.pinterest.com/pin/27654985208435505/"],
      captureMode: "off"
    }));
  });

  it("disables deep diagnostics for Pinterest provider URL recovery", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--provider=social/pinterest",
      "--url=https://www.pinterest.com/pin/27654985208435505/",
      "--capture-mode=deep"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", expect.objectContaining({
      harvest: true,
      providers: ["social/pinterest"],
      urls: ["https://www.pinterest.com/pin/27654985208435505/"],
      captureMode: "off"
    }));
  });

  it("rejects Pinterest provider query runs with non-canonical explicit URLs", async () => {
    await expect(runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=studio references",
      "--provider=social/pinterest",
      "--url=https://www.pinterest.com/search/pins/?q=studio"
    ]))).rejects.toThrow(
      "URL https://www.pinterest.com/search/pins/?q=studio is not a canonical social/pinterest reference URL for provider-scoped recovery."
    );

    await expect(runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=studio references",
      "--provider=social/pinterest",
      "--url=https://www.pinterest.com/create/pin/"
    ]))).rejects.toThrow(
      "URL https://www.pinterest.com/create/pin/ is not a canonical social/pinterest reference URL for provider-scoped recovery."
    );

    await expect(runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=studio references",
      "--provider=pinterest",
      "--url=https://www.pinterest.com/search/pins/?q=studio"
    ]))).rejects.toThrow(
      "URL https://www.pinterest.com/search/pins/?q=studio is not a canonical social/pinterest reference URL for provider-scoped recovery."
    );

    await expect(runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=studio references",
      "--provider=social/pinterest",
      "--provider=web/default",
      "--url=https://www.pinterest.com/search/pins/?q=studio"
    ]))).rejects.toThrow(
      "URL https://www.pinterest.com/search/pins/?q=studio is not a canonical social/pinterest reference URL for provider-scoped recovery."
    );

    await expect(runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=studio references",
      "--provider=social/pinterest",
      "--url=https://example.com/pin/27654985208435505/"
    ]))).rejects.toThrow(
      "URL https://example.com/pin/27654985208435505/ is not a canonical social/pinterest reference URL for provider-scoped recovery."
    );
  });

  it("accepts Pinterest provider query runs with canonical explicit URLs", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=studio references",
      "--provider=social/pinterest",
      "--url=https://www.pinterest.com/pin/27654985208435505/"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", expect.objectContaining({
      harvest: true,
      providers: ["social/pinterest"],
      query: "studio references",
      urls: ["https://www.pinterest.com/pin/27654985208435505/"]
    }));
  });

  it("rejects inspiredesign providers without query or compatible URLs", async () => {
    await expect(runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--provider=web/default"
    ]))).rejects.toThrow("Provider-scoped URL recovery requires at least one URL");

    await expect(runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--provider=web/default",
      "--url=https://www.pinterest.com/pin/27654985208435505/"
    ]))).rejects.toThrow("Provider web/default does not support URL-only site recipe recovery");
  });

  it("preserves inline output directory values containing equals signs", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=studio references",
      "--output-dir=/tmp/odb-output=a"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", expect.objectContaining({
      outputDir: "/tmp/odb-output=a"
    }));
  });

  it("reports legacy inspiredesign readiness metadata as diagnostic product authority", async () => {
    const cases = [
      { readiness: "diagnostic_only", rankedReferences: [] },
      { readiness: "ready", rankedReferences: [{ id: "reference-1" }] },
      {
        readiness: "ready",
        rankedReferences: [{ id: "reference-1", url: "https://www.pinterest.com/pin/11188699075430754/", capturedVia: ["snapshot_ready"], evidenceAuthority: "snapshot_ready" }],
        screenshotIndex: [{
          referenceId: "reference-1",
          url: "https://www.pinterest.com/pin/11188699075430754/",
          sourceUrl: "https://www.pinterest.com/pin/22288699075430754/",
          path: "visual-evidence/reference-1/viewport.png",
          sha256: "a".repeat(64),
          bytes: 4096,
          pinterestPageQuality: "pin_media",
          warnings: []
        }]
      },
      {
        readiness: "ready",
        rankedReferences: [
          { id: "pinterest-1", url: "https://www.pinterest.com/pin/11188699075430754/", capturedVia: ["snapshot_ready"], evidenceAuthority: "snapshot_ready" },
          { id: "pinterest-2", url: "https://www.pinterest.com/pin/77654985208435505/" }
        ]
      }
    ];

    for (const item of cases) {
      callDaemon.mockResolvedValueOnce({
        ok: true,
        rankedReferences: item.rankedReferences,
        ...(item.screenshotIndex ? { screenshotIndex: item.screenshotIndex } : {}),
        meta: {
          nextStepGuidance: {
            readiness: item.readiness
          }
        }
      });

      const result = await runInspiredesignCommand(makeArgs("inspiredesign", [
        "harvest",
        "--brief=Build a docs landing page contract",
        "--query=premium docs references"
      ]));

      expect(result.success).toBe(true);
      expect(result).toEqual(expect.objectContaining({
        productSuccess: false,
        artifactAuthority: "diagnostic_only",
        evidenceAuthority: "diagnostic_only"
      }));
      expect(result.message).toContain(`guidanceReadiness=${item.readiness}`);
      expect(result.message).toContain("productSuccess=false artifactAuthority=diagnostic_only evidenceAuthority=diagnostic_only");
      expect(result.message).not.toContain(` readiness=${item.readiness}`);
    }
  });

  it("trusts daemon-provided inspiredesign product authority before deriving legacy fields", async () => {
    callDaemon.mockResolvedValueOnce({
      ok: true,
      ready: true,
      readiness: "ready",
      harvestReadiness: "ready",
      productSuccess: false,
      artifactAuthority: "diagnostic_only",
      evidenceAuthority: "diagnostic_only",
      rankedReferenceCount: 1,
      authoritativeReferenceCount: 0,
      snapshotReadyReferenceCount: 0,
      motionReadyReferenceCount: 0,
      rankedReferences: [{
        id: "reference-1",
        url: "https://www.pinterest.com/pin/11188699075430754/",
        capturedVia: ["snapshot_ready"],
        evidenceAuthority: "snapshot_ready"
      }],
      screenshotIndex: [{
        referenceId: "reference-1",
        url: "https://www.pinterest.com/pin/11188699075430754/",
        sourceUrl: "https://www.pinterest.com/pin/11188699075430754/",
        path: "visual-evidence/reference-1/viewport.png",
        sha256: "a".repeat(64),
        bytes: 4096,
        warnings: []
      }]
    });

    const result = await runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=premium docs references"
    ]));

    expect(result).toEqual(expect.objectContaining({
      success: true,
      ready: false,
      readiness: "ready",
      guidanceReady: true,
      guidanceReadiness: "ready",
      productSuccess: false,
      artifactAuthority: "diagnostic_only",
      evidenceAuthority: "diagnostic_only",
      rankedReferenceCount: 1,
      authoritativeReferenceCount: 0
    }));
    expect(result.message).toContain("guidanceReadiness=ready");
    expect(result.message).toContain("productSuccess=false artifactAuthority=diagnostic_only evidenceAuthority=diagnostic_only");
    expect(result.message).not.toContain(" readiness=ready");
  });

  it("reads top-level next-step readiness and exposes explicit product authority", async () => {
    callDaemon.mockResolvedValueOnce({
      ok: true,
      nextStepGuidance: { readiness: "ready" },
      productSuccess: true,
      artifactAuthority: "product_ready",
      evidenceAuthority: "snapshot_ready",
      rankedReferences: [{
        id: "reference-1",
        url: "https://studio.example/reference/editorial-lighting",
        evidenceAuthority: "snapshot_ready"
      }],
      screenshotIndex: [{
        referenceId: "reference-1",
        url: "https://studio.example/reference/editorial-lighting",
        sourceUrl: "https://studio.example/reference/editorial-lighting",
        path: "visual-evidence/reference-1/viewport.png",
        sha256: "a".repeat(64),
        bytes: 4096,
        warnings: []
      }]
    });

    const result = await runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=premium docs references"
    ]));

    expect(result.message).toContain("readiness=ready");
    expect(result).toEqual(expect.objectContaining({
      success: true,
      ready: true,
      readiness: "ready",
      productSuccess: true,
      artifactAuthority: "product_ready",
      evidenceAuthority: "snapshot_ready",
      rankedReferenceCount: 1,
      authoritativeReferenceCount: 1,
      snapshotReadyReferenceCount: 1,
      motionReadyReferenceCount: 0,
      pinMediaReadyReferenceCount: 0
    }));
  });

  it("keeps self-reported inspiredesign product readiness diagnostic without artifact evidence", async () => {
    callDaemon.mockResolvedValueOnce({
      ok: true,
      nextStepGuidance: { readiness: "ready" },
      productSuccess: true,
      artifactAuthority: "product_ready",
      evidenceAuthority: "pin_media_ready"
    });

    const result = await runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--query=premium docs references"
    ]));

    expect(result).toEqual(expect.objectContaining({
      success: true,
      ready: false,
      readiness: "ready",
      guidanceReady: true,
      guidanceReadiness: "ready",
      productSuccess: false,
      artifactAuthority: "diagnostic_only",
      evidenceAuthority: "diagnostic_only",
      rankedReferenceCount: 0,
      authoritativeReferenceCount: 0
    }));
    expect(result.message).toContain("guidanceReadiness=ready");
    expect(result.message).toContain("productSuccess=false artifactAuthority=diagnostic_only evidenceAuthority=diagnostic_only");
    expect(result.message).not.toContain(" readiness=ready");
  });

  it("rejects invalid inspiredesign harvest bounds and visual modes", async () => {
    await expect(runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--max-references=11"
    ]))).rejects.toThrow("Invalid --max-references: 11");

    await expect(runInspiredesignCommand(makeArgs("inspiredesign", [
      "harvest",
      "--brief=Build a docs landing page contract",
      "--visual-evidence=always"
    ]))).rejects.toThrow("Invalid --visual-evidence: always");
  });

  it("defaults inspiredesign capture mode to off when no urls are supplied", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "run",
      "--brief=Design system baseline"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", expect.objectContaining({
      brief: "Design system baseline",
      captureMode: "off",
      mode: "compact"
    }));
  });

  it("forces inspiredesign capture mode to deep when urls are supplied", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "run",
      "--brief=Design system baseline",
      "--url=https://example.com/reference"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", expect.objectContaining({
      brief: "Design system baseline",
      urls: ["https://example.com/reference"],
      captureMode: "deep",
      mode: "compact"
    }));
  });

  it("overrides explicit off capture mode to deep when urls are supplied", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runInspiredesignCommand(makeArgs("inspiredesign", [
      "run",
      "--brief=Design system baseline",
      "--url=https://example.com/reference",
      "--capture-mode=off"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("inspiredesign.run", expect.objectContaining({
      brief: "Design system baseline",
      urls: ["https://example.com/reference"],
      captureMode: "deep",
      mode: "compact"
    }));
  });

  it("surfaces inspiredesign success follow-through guidance", async () => {
    callDaemon.mockResolvedValue({
      followthroughSummary: buildInspiredesignFollowthroughSummary(),
      suggestedNextAction: buildInspiredesignNextStep()
    });

    const result = await runInspiredesignCommand(makeArgs("inspiredesign", [
      "run",
      "--brief=Design system baseline"
    ]));

    expect(result).toEqual({
      success: true,
      ready: false,
      readiness: "unknown",
      guidanceReady: false,
      guidanceReadiness: "unknown",
      harvestReadiness: "unknown",
      productSuccess: false,
      artifactAuthority: "diagnostic_only",
      evidenceAuthority: "diagnostic_only",
      rankedReferenceCount: 0,
      authoritativeReferenceCount: 0,
      snapshotReadyReferenceCount: 0,
      motionReadyReferenceCount: 0,
      pinMediaReadyReferenceCount: 0,
      message: `Inspiredesign workflow completed. ${buildInspiredesignFollowthroughSummary()} Next step: ${buildInspiredesignNextStep()} guidanceReadiness=unknown productSuccess=false artifactAuthority=diagnostic_only evidenceAuthority=diagnostic_only`,
      data: {
        followthroughSummary: buildInspiredesignFollowthroughSummary(),
        suggestedNextAction: buildInspiredesignNextStep()
      }
    });
  });

  it("surfaces inspiredesign provider follow-up requirements before generic follow-through", async () => {
    callDaemon.mockResolvedValue({
      followthroughSummary: buildInspiredesignFollowthroughSummary(),
      suggestedNextAction: buildInspiredesignNextStep(),
      meta: {
        primaryConstraintSummary: "Deep capture failed for 1 reference.",
        primaryConstraint: {
          guidance: {
            recommendedNextCommands: [
              "Retry deep capture for https://example.com/reference after restoring the required browser session state."
            ]
          }
        }
      }
    });

    const result = await runInspiredesignCommand(makeArgs("inspiredesign", [
      "run",
      "--brief=Design system baseline",
      "--url=https://example.com/reference"
    ]));

    expect(result).toEqual({
      success: true,
      ready: false,
      readiness: "unknown",
      guidanceReady: false,
      guidanceReadiness: "unknown",
      harvestReadiness: "unknown",
      productSuccess: false,
      artifactAuthority: "diagnostic_only",
      evidenceAuthority: "diagnostic_only",
      rankedReferenceCount: 0,
      authoritativeReferenceCount: 0,
      snapshotReadyReferenceCount: 0,
      motionReadyReferenceCount: 0,
      pinMediaReadyReferenceCount: 0,
      message: "Inspiredesign workflow completed with provider follow-up required: Deep capture failed for 1 reference. Next step: Retry deep capture for https://example.com/reference after restoring the required browser session state. guidanceReadiness=unknown productSuccess=false artifactAuthority=diagnostic_only evidenceAuthority=diagnostic_only",
      data: {
        followthroughSummary: buildInspiredesignFollowthroughSummary(),
        suggestedNextAction: buildInspiredesignNextStep(),
        meta: {
          primaryConstraintSummary: "Deep capture failed for 1 reference.",
          primaryConstraint: {
            guidance: {
              recommendedNextCommands: [
                "Retry deep capture for https://example.com/reference after restoring the required browser session state."
              ]
            }
          }
        }
      }
    });
  });

  it.each([
    {
      name: "research",
      label: "Research workflow",
      run: () => runResearchCommand(makeArgs("research", [
        "run",
        "--topic=browser automation"
      ]))
    },
    {
      name: "shopping",
      label: "Shopping workflow",
      run: () => runShoppingCommand(makeArgs("shopping", [
        "run",
        "--query=usb hub"
      ]))
    },
    {
      name: "product-video",
      label: "Product video asset workflow",
      run: () => runProductVideoCommand(makeArgs("product-video", [
        "run",
        "--product-name=Sample Product"
      ]))
    }
  ])("surfaces $name success follow-through guidance", async ({ label, run }) => {
    callDaemon.mockResolvedValue({
      followthroughSummary: "Review the returned bundle before publishing the workflow outcome.",
      suggestedNextAction: "Inspect the artifact path and rerun with tighter inputs if you need stronger evidence."
    });

    const result = await run();

    expect(result).toEqual({
      success: true,
      message: `${label} completed. Review the returned bundle before publishing the workflow outcome. Next step: Inspect the artifact path and rerun with tighter inputs if you need stronger evidence.`,
      data: {
        followthroughSummary: "Review the returned bundle before publishing the workflow outcome.",
        suggestedNextAction: "Inspect the artifact path and rerun with tighter inputs if you need stronger evidence."
      }
    });
  });

  it("parses and dispatches product-video run payload", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runProductVideoCommand(makeArgs("product-video", [
      "run",
      "--product-name=Sample Product",
      "--provider-hint=shopping/amazon",
      "--include-screenshots=false",
      "--include-all-images=true",
      "--include-copy=false",
      "--output-dir=/tmp/assets",
      "--ttl-hours=48"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("product.video.run", {
      product_url: undefined,
      product_name: "Sample Product",
      provider_hint: "shopping/amazon",
      include_screenshots: false,
      include_all_images: true,
      include_copy: false,
      output_dir: "/tmp/assets",
      ttl_hours: 48,
      timeoutMs: 120000,
      browserMode: undefined,
      useCookies: undefined,
      challengeAutomationMode: undefined,
      cookiePolicyOverride: undefined
    });
  });

  it("supports explicit timeout for product-video workflows", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runProductVideoCommand(makeArgs("product-video", [
      "run",
      "--product-url=https://example.com/item",
      "--timeout-ms=45000"
    ]));

    expect(callDaemon).toHaveBeenCalledWith("product.video.run", {
      product_url: "https://example.com/item",
      product_name: undefined,
      provider_hint: undefined,
      include_screenshots: undefined,
      include_all_images: undefined,
      include_copy: undefined,
      output_dir: defaultWorkflowOutputDir,
      ttl_hours: undefined,
      timeoutMs: 45000,
      browserMode: undefined,
      useCookies: undefined,
      challengeAutomationMode: undefined,
      cookiePolicyOverride: undefined
    });
  });

  it("parses workflow cookie overrides and forwards them", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runResearchCommand(makeArgs("research", [
      "run",
      "--topic=cookie routing",
      "--use-cookies=false",
      "--cookie-policy-override=required"
    ]));
    expect(callDaemon).toHaveBeenLastCalledWith("research.run", expect.objectContaining({
      topic: "cookie routing",
      useCookies: false,
      cookiePolicyOverride: "required"
    }));

    await runShoppingCommand(makeArgs("shopping", [
      "run",
      "--query=wireless keyboard",
      "--use-cookies",
      "--cookie-policy=auto"
    ]));
    expect(callDaemon).toHaveBeenLastCalledWith("shopping.run", expect.objectContaining({
      query: "wireless keyboard",
      useCookies: true,
      cookiePolicyOverride: "auto"
    }), {
      timeoutMs: 180000
    });

    await runProductVideoCommand(makeArgs("product-video", [
      "run",
      "--product-name=Device",
      "--use-cookies=false",
      "--cookie-policy-override=off"
    ]));
    expect(callDaemon).toHaveBeenLastCalledWith("product.video.run", expect.objectContaining({
      product_name: "Device",
      useCookies: false,
      cookiePolicyOverride: "off"
    }));
  });

  it("propagates challengeAutomationMode across workflow commands", async () => {
    callDaemon.mockResolvedValue({ ok: true });

    await runResearchCommand(makeArgs("research", [
      "run",
      "--topic=browser automation",
      "--challenge-automation-mode=browser_with_helper"
    ]));
    expect(callDaemon).toHaveBeenLastCalledWith("research.run", expect.objectContaining({
      topic: "browser automation",
      challengeAutomationMode: "browser_with_helper"
    }));

    await runShoppingCommand(makeArgs("shopping", [
      "run",
      "--query=wireless keyboard",
      "--challenge-automation-mode",
      "browser"
    ]));
    expect(callDaemon).toHaveBeenLastCalledWith("shopping.run", expect.objectContaining({
      query: "wireless keyboard",
      challengeAutomationMode: "browser"
    }), {
      timeoutMs: 180000
    });

    await runProductVideoCommand(makeArgs("product-video", [
      "run",
      "--product-url=https://example.com/item",
      "--challenge-automation-mode",
      "off"
    ]));
    expect(callDaemon).toHaveBeenLastCalledWith("product.video.run", expect.objectContaining({
      product_url: "https://example.com/item",
      challengeAutomationMode: "off"
    }));
  });

  it("enforces run subcommand and required input", async () => {
    await expect(runResearchCommand(makeArgs("research", ["status"]))).rejects.toThrow("Usage: opendevbrowser research run");
    await expect(runResearchCommand(makeArgs("research", ["run", "--topic=browser", "--browser-mode=bad"]))).rejects.toThrow("Invalid --browser-mode: bad");
    await expect(runShoppingCommand(makeArgs("shopping", ["run"]))).rejects.toThrow("Missing --query");
    await expect(runShoppingCommand(makeArgs("shopping", ["run", "--query=macbook", "--browser-mode=bad"]))).rejects.toThrow("Invalid --browser-mode: bad");
    await expect(runProductVideoCommand(makeArgs("product-video", ["run", "--product-url=https://example.com", "--browser-mode=bad"]))).rejects.toThrow("Invalid --browser-mode: bad");
    await expect(runInspiredesignCommand(makeArgs("inspiredesign", ["run", "--brief=Design system", "--browser-mode=bad"]))).rejects.toThrow("Invalid --browser-mode: bad");
    await expect(runMacroResolve(makeArgs("macro-resolve", ["--expression=@web.search(\"x\")", "--browser-mode=bad"]))).rejects.toThrow("Invalid --browser-mode: bad");
    await expect(runMacroResolve(makeArgs("macro-resolve", ["--expression=@web.search(\"x\")", "--browser-mode=managed"]))).rejects.toThrow("--browser-mode requires --execute for macro-resolve");
    await expect(runMacroResolve(makeArgs("macro-resolve", ["--expression=@web.search(\"x\")", "--use-cookies"]))).rejects.toThrow("--use-cookies requires --execute for macro-resolve");
    await expect(runMacroResolve(makeArgs("macro-resolve", ["--expression=@web.search(\"x\")", "--challenge-automation-mode=browser"]))).rejects.toThrow("--challenge-automation-mode requires --execute for macro-resolve");
    await expect(runMacroResolve(makeArgs("macro-resolve", ["--expression=@web.search(\"x\")", "--cookie-policy=required"]))).rejects.toThrow("--cookie-policy requires --execute for macro-resolve");
    await expect(runProductVideoCommand(makeArgs("product-video", ["run"]))).rejects.toThrow("Missing --product-url or --product-name");
    await expect(runInspiredesignCommand(makeArgs("inspiredesign", ["run"]))).rejects.toThrow("Missing --brief");
    await expect(runInspiredesignCommand(makeArgs("inspiredesign", ["run", "--brief=Design system", "--capture-mode=wrong"]))).rejects.toThrow("Invalid --capture-mode: wrong");
  });
});
