import { Given, Then, When } from "@badeball/cypress-cucumber-preprocessor";

// Mocks the API via cy.intercept — this job doesn't run a live backend (see
// .github/workflows/ci.yml); apps/api's own Mocha suite covers real API
// behavior against a real database (see test/sources.test.ts,
// test/planning.test.ts).
//
// planning.feature's journey crosses both `RecipePickerDialog` (browsing an
// external source from a planning slot) and the import review screen
// (`ImportRecipePage`) it hands off to — same "each spec's own
// self-contained fixtures" precedent recipe-sources.ts already sets (the
// Cucumber preprocessor's step lookup isn't global across cypress/e2e/, see
// its own comment for the full reasoning), so most of what's below mirrors
// recipe-sources.ts's fixtures rather than importing them.

Given("the recipe catalog contains nothing", () => {
  cy.intercept("GET", /\/recipes\?/, { statusCode: 200, body: [] });
});

Given("the household has enabled TheMealDB", () => {
  cy.intercept("GET", "**/house/current/sources", { statusCode: 200, body: [1] });
});

Given("browsing TheMealDB returns some items", () => {
  cy.intercept("GET", "**/sources/theMealDb/browse*", {
    statusCode: 200,
    body: {
      items: [
        {
          externalId: "52795",
          title: "Chicken Handi",
          picture: null,
          url: "https://www.themealdb.com/meal/52795",
          alreadyImported: true,
          recipeId: 2,
        },
        {
          externalId: "9999",
          title: "Fish Pie",
          picture: null,
          url: "https://www.themealdb.com/meal/9999",
          alreadyImported: false,
          recipeId: null,
        },
        // Draft's own preview ("previewing TheMealDB item ... is fully
        // resolved") has nothing left for a person to fix — unlike "Fish
        // Pie" above, exercises the transparent-import path instead of the
        // review screen.
        {
          externalId: "7777",
          title: "Ratatouille",
          picture: null,
          url: "https://www.themealdb.com/meal/7777",
          alreadyImported: false,
          recipeId: null,
        },
      ],
      nextCursor: null,
    },
  });
});

Given("previewing TheMealDB item {string} is available", (externalId: string) => {
  cy.intercept("GET", `**/sources/theMealDb/preview/${externalId}`, {
    statusCode: 200,
    body: {
      sourceKey: "theMealDb",
      externalId,
      name: "Fish Pie",
      description: null,
      picture: null,
      portions: 4,
      sourceUrl: "https://www.themealdb.com/meal/9999",
      ingredients: [
        {
          rawText: "1 onion",
          quantity: 1,
          ingredient: {
            id: 1,
            key: "onion",
            icon: "VEGETABLE",
            category: "freshProduce",
            subcategory: "vegetables",
            reproducible: false,
            allergens: [],
            diets: [],
          },
          unit: null,
        },
        { rawText: "some mystery paste", quantity: null, ingredient: null, unit: null },
      ],
      steps: [{ description: "Cuire à la poêle.", picture: null, techSteps: [] }],
    },
  });
});

// Unlike "previewing TheMealDB item ... is available" above, every
// ingredient line here already resolved to a real ingredient/unit/quantity
// — `tryBuildCompleteImport` (RecipePickerDialog.tsx) accepts a draft
// shaped exactly like this one as-is, no review screen needed.
Given(
  "previewing TheMealDB item {string} is fully resolved as {string}",
  (externalId: string, name: string) => {
    cy.intercept("GET", `**/sources/theMealDb/preview/${externalId}`, {
      statusCode: 200,
      body: {
        sourceKey: "theMealDb",
        externalId,
        name,
        description: null,
        picture: null,
        portions: 4,
        sourceUrl: `https://www.themealdb.com/meal/${externalId}`,
        ingredients: [
          {
            rawText: "1 onion",
            quantity: 1,
            ingredient: {
              id: 1,
              key: "onion",
              icon: "VEGETABLE",
              category: "freshProduce",
              subcategory: "vegetables",
              reproducible: false,
              allergens: [],
              diets: [],
            },
            unit: { id: 1, key: "piece", type: "COUNT", toBaseFactor: 1 },
          },
        ],
        steps: [{ description: "Cuire à la poêle.", picture: null, techSteps: [] }],
      },
    });
  },
);

Given(
  "importing item {string} will succeed and return id {int}",
  (externalId: string, id: number) => {
    cy.intercept("POST", `**/sources/theMealDb/import/${externalId}`, {
      statusCode: 201,
      body: { id },
    }).as("importItem");
  },
);

Given("adding recipe {int} to the planning will succeed", (recipeId: number) => {
  cy.intercept("POST", "**/planning/items", {
    statusCode: 201,
    body: {
      id: 2,
      weekDay: "lundi",
      meal: "petit-dejeuner",
      portions: 4,
      recipe: { id: recipeId, name: "Ratatouille" },
    },
  }).as("addPlanningItem");
});

Then(
  "the import request for {string} should have included the name {string}",
  (_externalId: string, name: string) => {
    cy.wait("@importItem").its("request.body.name").should("eq", name);
  },
);

// Covers every reference catalog both `RecipePickerDialog` (ingredients/
// diets, for its own filters) and `ImportRecipePage` (ingredients/diets/
// units, for the review form) fetch — same endpoints, one fixture for both.
Given("the ingredient and diet catalog is available for import", () => {
  cy.intercept("GET", "**/reference/ingredients", {
    statusCode: 200,
    body: [
      {
        id: 1,
        key: "onion",
        icon: "VEGETABLE",
        category: "freshProduce",
        subcategory: "vegetables",
        allergens: [],
        diets: [],
      },
      {
        id: 2,
        key: "salt",
        icon: "SPICE",
        category: "condimentsAndSpices",
        subcategory: "spices",
        allergens: [],
        diets: [],
      },
    ],
  });
  cy.intercept("GET", "**/reference/diets", {
    statusCode: 200,
    body: [{ id: 1, key: "omnivore" }],
  });
  cy.intercept("GET", "**/reference/units", {
    statusCode: 200,
    body: [{ id: 1, key: "piece", type: "COUNT", toBaseFactor: 1 }],
  });
});

Given("importing the previewed item will succeed and return id {int}", (id: number) => {
  cy.intercept("POST", "**/sources/theMealDb/import/9999", {
    statusCode: 201,
    body: { id },
  }).as("importRecipe");
});

Given("adding the imported recipe to the planning will succeed", () => {
  cy.intercept("POST", "**/planning/items", {
    statusCode: 201,
    body: {
      id: 1,
      weekDay: "lundi",
      meal: "petit-dejeuner",
      portions: 4,
      recipe: { id: 99, name: "Fish Pie" },
    },
  }).as("addPlanningItem");
});

// Every scenario here confirms/imports without ever leaving "/" (the
// footer's "Confirmer" patches the grid locally via `PlanningPage`'s own
// `onAdded` — `RecipePickerDialog`'s doc comment — rather than navigating
// away and back), so unlike a real cross-route remount, this fixture never
// needs to reflect what's been added: the grid picks it up from local
// state, not a fresh fetch.
Then("the recipe picker dialog should be closed", () => {
  cy.get(".dialog-panel").should("not.exist");
});

// The very first "+" in DOM order is Lundi's Petit-déjeuner cell (`MEALS`'s
// first entry × `WEEK_DAYS`'s first entry, see `PlanningGrid`) — the exact
// slot this feature's fixtures above (weekDay "lundi", meal
// "petit-dejeuner") are written against.
When("I click the add button for the first empty planning slot", () => {
  cy.get(".add-recipe-btn").first().click();
});

When("I click the source item {string}", (title: string) => {
  cy.contains(".recipe-table__name", title).click();
});

When("I choose an ingredient for the unresolved line {string}", (rawText: string) => {
  cy.contains(".import-recipe__unresolved-row", rawText)
    .contains("button", "Choisir un ingrédient")
    .click();
});

Then(
  "the planning add request should have included recipe {int}, weekDay {string}, meal {string}, and portions {int}",
  (recipeId: number, weekDay: string, meal: string, portions: number) => {
    cy.wait("@addPlanningItem")
      .its("request.body")
      .should("deep.include", { recipeId, weekDay, meal, portions });
  },
);

Then(
  "the recipe {string} should appear in the first planning slot with {int} portions",
  (name: string, portions: number) => {
    cy.get(".planning-grid tbody tr")
      .first()
      .within(() => {
        cy.contains(".recipe-chip", `${name} · ×${portions}`).should("be.visible");
      });
  },
);
