import { http, HttpResponse } from "msw";
import preview from "../../.storybook/preview.tsx";
import { sleep } from "../sleep.ts";
import type { CurrentUser, SessionInfo } from "../types.ts";
import { JoinCard } from "./JoinCard.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: {
      refreshTokens: {
        failed: () => {
          return http.post(
            "http://mock.api/v1/sessions/access-tokens",
            async () => {
              await simulateNetwork();
              return HttpResponse.text("Refresh token expired or missing", {
                status: 401,
              });
            },
          );
        },
      },

      getCurrentUser: {
        success: (currentUser: CurrentUser) => {
          return http.get("http://mock.api/v1/users/me", async () => {
            await simulateNetwork();
            return HttpResponse.json(currentUser);
          });
        },

        noConnection: () => {
          return http.get("http://mock.api/v1/users/me", async () => {
            return HttpResponse.error();
          });
        },

        unknownFailure: () => {
          return http.get("http://mock.api/v1/users/me", async () => {
            await simulateNetwork();
            return HttpResponse.text("Internal server error", { status: 500 });
          });
        },

        /**
         * Auth cookies no longer valid to make this request.
         */
        notAuthenticated: () => {
          return http.get("http://mock.api/v1/users/me", async () => {
            await simulateNetwork();
            return HttpResponse.text("Not authenticated", { status: 401 });
          });
        },
      },

      join: {
        success(currentUser: CurrentUser) {
          return http.post("http://mock.api/v1/users", async () => {
            await simulateNetwork();

            return HttpResponse.json({
              currentUser,
              sessionInfo,
              tokenId: "1",
            });
          });
        },

        emailTaken() {
          return http.post("http://mock.api/v1/users", async () => {
            await simulateNetwork();

            return HttpResponse.text("Duplicate unique key", { status: 409 });
          });
        },

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

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

const meta = preview.meta({
  title: "Account/Join Card",
  component: JoinCard,
  tags: ["autodocs"],
  args: {
    defaultValues: {},
  },
  parameters: {
    msw: {
      handlers: {
        refresh: handlers.refreshTokens.failed(),
        currentUser: handlers.getCurrentUser.notAuthenticated(),
        join: handlers.join.success(data.john),
        verify: handlers.verify.success(),
      },
    },
  },
});

/**
 * 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 is associated
 * with an existing user.
 */
export const EmailTakenOnJoin = meta.story({
  parameters: {
    msw: {
      handlers: {
        join: handlers.join.emailTaken(),
      },
    },
  },
});

/**
 * 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 UnknownFailureOnJoin = meta.story({
  parameters: {
    msw: {
      handlers: {
        join: handlers.join.unknownFailure(),
      },
    },
  },
});

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

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

/**
 * The proactive user-session check returns a user account.
 *
 * The user can continue to use the app, or log out.
 */
export const AlreadyLoggedIn = meta.story({
  parameters: {
    msw: {
      handlers: {
        currentUser: handlers.getCurrentUser.success(data.john),
      },
    },
  },
});

/**
 * The proactive user session check has failed due to connection issues.
 */
export const NoConnection = meta.story({
  parameters: {
    msw: {
      handlers: {
        currentUser: handlers.getCurrentUser.noConnection(),
      },
    },
  },
});

/**
 * The proactive user session check has failed due to an error that doesn't
 * carry any special meaning.
 */
export const UnknownFailure = meta.story({
  parameters: {
    msw: {
      handlers: {
        currentUser: handlers.getCurrentUser.unknownFailure(),
      },
    },
  },
});
