import { http, HttpResponse } from "msw";
import preview from "../../.storybook/preview.tsx";
import { sleep } from "../sleep.ts";
import type { CurrentUser, SessionInfo } from "../types.ts";
import { PasswordResetCard } from "./PasswordResetCard.tsx";

function createMocks(options?: { responseSpeed?: number }) {
  const simulateNetwork = () => sleep(options?.responseSpeed ?? 2000);

  const john: CurrentUser = {
    id: "john",
    email: "john@example.com",
    isVerified: true,
  };

  const sessionInfo: SessionInfo = {
    createdTs: 123,
    expiresTs: 123,
  };

  return {
    data: { john },
    handlers: {
      requestPasswordReset: {
        success: () => {
          return http.post(
            "http://mock.api/v1/password-reset-tokens",
            async () => {
              await simulateNetwork();
              return HttpResponse.json({ message: "OK", tokenId: "1" });
            },
          );
        },
        userNotFound: () => {
          return http.post(
            "http://mock.api/v1/password-reset-tokens",
            async () => {
              await simulateNetwork();
              return HttpResponse.text("User not found", { status: 404 });
            },
          );
        },
        unknownFailure: () => {
          return http.post(
            "http://mock.api/v1/password-reset-tokens",
            async () => {
              await simulateNetwork();
              return HttpResponse.text("Internal server error", {
                status: 500,
              });
            },
          );
        },
      },

      checkPasswordResetCode: {
        success() {
          return http.get(
            "http://mock.api/v1/password-reset-tokens/:id",
            async () => {
              await simulateNetwork();
              return HttpResponse.json({ message: "OK" });
            },
          );
        },
        notFoundOrExpired() {
          return http.get(
            "http://mock.api/v1/password-reset-tokens/:id",
            async () => {
              await simulateNetwork();
              return HttpResponse.text("Not found", { status: 404 });
            },
          );
        },
        unknownFailure() {
          return http.get(
            "http://mock.api/v1/password-reset-tokens/:id",
            async () => {
              await simulateNetwork();
              return HttpResponse.text("Internal server error", {
                status: 500,
              });
            },
          );
        },
      },

      setNewPassword: {
        success() {
          return http.put(
            "http://mock.api/v1/password-reset-tokens/:id",
            async () => {
              await simulateNetwork();
              return HttpResponse.json({ message: "OK" });
            },
          );
        },
        notFoundOrExpired() {
          return http.put(
            "http://mock.api/v1/password-reset-tokens/:id",
            async () => {
              await simulateNetwork();
              return HttpResponse.text("Not found", { status: 404 });
            },
          );
        },
        unknownFailure() {
          return http.put(
            "http://mock.api/v1/password-reset-tokens/:id",
            async () => {
              await simulateNetwork();
              return HttpResponse.text("Internal server error", {
                status: 500,
              });
            },
          );
        },
      },

      createNewSession: {
        success: (currentUser: CurrentUser) => {
          return http.post("http://mock.api/v1/sessions", async () => {
            await simulateNetwork();
            return HttpResponse.json({ currentUser, sessionInfo });
          });
        },
      },
    },
  };
}

const { data, handlers } = createMocks({ responseSpeed: 700 });

const meta = preview.meta({
  title: "Account/Password Reset Page",
  component: PasswordResetCard,
  tags: ["autodocs"],
  args: {},
  parameters: {
    msw: {
      handlers: {
        request: handlers.requestPasswordReset.success(),
        check: handlers.checkPasswordResetCode.success(),
        set: handlers.setNewPassword.success(),
        login: handlers.createNewSession.success(data.john),
      },
    },
  },
});

/**
 * The default case in which all steps of the flow succeed.
 */
export const Success = meta.story({});

/**
 * In this case, the initial step fails because the email address was not
 * found in our database.
 */
export const UserNotFoundOnRequestCode = meta.story({
  parameters: {
    msw: {
      handlers: {
        request: handlers.requestPasswordReset.userNotFound(),
      },
    },
  },
});

/**
 * In this case, the initial step fails for a reason that doesn't have any
 * special handling in this location. E.g. network error, server error, etc.
 */
export const UnknownFailureOnRequestCode = meta.story({
  parameters: {
    msw: {
      handlers: {
        request: handlers.requestPasswordReset.unknownFailure(),
      },
    },
  },
});

/**
 * In this case, the check code step fails because the token has expired,
 * or is incorrect.
 */
export const NotFoundOrExpiredOnCheckCode = meta.story({
  parameters: {
    msw: {
      handlers: {
        check: handlers.checkPasswordResetCode.notFoundOrExpired(),
      },
    },
  },
});

/**
 * In this case, the check step fails for a reason that doesn't have any
 * special handling in this location. E.g. network error, server error, etc.
 */
export const UnknownFailureOnCheckCode = meta.story({
  parameters: {
    msw: {
      handlers: {
        check: handlers.checkPasswordResetCode.unknownFailure(),
      },
    },
  },
});

/**
 * In this case, the final set password step fails because the token has
 * expired.
 *
 * Note that technically the token could also be incorrect/not found, but
 * because the user has gotten through the check step, that would require
 * some very strange set of circumstances.
 */
export const NotFoundOrExpiredOnSetPassword = meta.story({
  parameters: {
    msw: {
      handlers: {
        set: handlers.setNewPassword.notFoundOrExpired(),
      },
    },
  },
});

/**
 * In this case, the set password step fails for a reason that doesn't have any
 * special handling in this location. E.g. network error, server error, etc.
 */
export const UnknownFailureOnSetPassword = meta.story({
  parameters: {
    msw: {
      handlers: {
        set: handlers.setNewPassword.unknownFailure(),
      },
    },
  },
});
