import { describe, it, expect } from "vitest";
import { parseAuthError, getErrorMessage } from "./auth-errors";
import type { AuthErrorCode, ParsedAuthError } from "./auth-errors";

/** Words that must never appear in PDA-friendly messages. */
const FORBIDDEN_WORDS = [
  "overdue",
  "urgent",
  "must",
  "critical",
  "required",
  "error",
  "failed",
  "failure",
];

describe("parseAuthError", (): void => {
  it("should classify TypeError('Failed to fetch') as network_error", (): void => {
    const result: ParsedAuthError = parseAuthError(new TypeError("Failed to fetch"));
    expect(result.code).toBe("network_error");
    expect(result.retryable).toBe(true);
  });

  it("should classify TypeError with 'fetch' anywhere in message as network_error", (): void => {
    const result: ParsedAuthError = parseAuthError(new TypeError("Could not fetch resource"));
    expect(result.code).toBe("network_error");
  });

  it("should classify Error('Unauthorized') as invalid_credentials", (): void => {
    const result: ParsedAuthError = parseAuthError(new Error("Unauthorized"));
    expect(result.code).toBe("invalid_credentials");
    expect(result.retryable).toBe(false);
  });

  it("should classify Error('Forbidden') as invalid_credentials", (): void => {
    const result: ParsedAuthError = parseAuthError(new Error("Forbidden"));
    expect(result.code).toBe("invalid_credentials");
  });

  it("should classify HTTP 401 response as invalid_credentials", (): void => {
    const result: ParsedAuthError = parseAuthError({ status: 401 });
    expect(result.code).toBe("invalid_credentials");
    expect(result.retryable).toBe(false);
  });

  it("should classify HTTP 403 response as invalid_credentials", (): void => {
    const result: ParsedAuthError = parseAuthError({ status: 403 });
    expect(result.code).toBe("invalid_credentials");
  });

  it("should classify HTTP 429 response as rate_limited", (): void => {
    const result: ParsedAuthError = parseAuthError({ status: 429 });
    expect(result.code).toBe("rate_limited");
    expect(result.retryable).toBe(false);
  });

  it("should classify HTTP 500 response as server_error", (): void => {
    const result: ParsedAuthError = parseAuthError({ status: 500 });
    expect(result.code).toBe("server_error");
    expect(result.retryable).toBe(true);
  });

  it("should classify HTTP 502 response as server_error", (): void => {
    const result: ParsedAuthError = parseAuthError({ status: 502 });
    expect(result.code).toBe("server_error");
  });

  it("should classify HTTP 503 response as server_error", (): void => {
    const result: ParsedAuthError = parseAuthError({ status: 503 });
    expect(result.code).toBe("server_error");
  });

  it("should classify string 'access_denied' as access_denied", (): void => {
    const result: ParsedAuthError = parseAuthError("access_denied");
    expect(result.code).toBe("access_denied");
    expect(result.retryable).toBe(false);
  });

  it("should classify string 'session_expired' as session_expired", (): void => {
    const result: ParsedAuthError = parseAuthError("session_expired");
    expect(result.code).toBe("session_expired");
    expect(result.retryable).toBe(false);
  });

  it("should classify string 'rate_limited' as rate_limited", (): void => {
    const result: ParsedAuthError = parseAuthError("rate_limited");
    expect(result.code).toBe("rate_limited");
  });

  it("should classify string 'server_error' as server_error", (): void => {
    const result: ParsedAuthError = parseAuthError("server_error");
    expect(result.code).toBe("server_error");
    expect(result.retryable).toBe(true);
  });

  it("should classify string 'network_error' as network_error", (): void => {
    const result: ParsedAuthError = parseAuthError("network_error");
    expect(result.code).toBe("network_error");
    expect(result.retryable).toBe(true);
  });

  it("should classify unknown string as unknown", (): void => {
    const result: ParsedAuthError = parseAuthError("something_weird");
    expect(result.code).toBe("unknown");
    expect(result.retryable).toBe(false);
  });

  it("should classify null as unknown", (): void => {
    const result: ParsedAuthError = parseAuthError(null);
    expect(result.code).toBe("unknown");
  });

  it("should classify undefined as unknown", (): void => {
    const result: ParsedAuthError = parseAuthError(undefined);
    expect(result.code).toBe("unknown");
  });

  it("should classify a number as unknown", (): void => {
    const result: ParsedAuthError = parseAuthError(42);
    expect(result.code).toBe("unknown");
  });

  it("should classify an empty object as unknown", (): void => {
    const result: ParsedAuthError = parseAuthError({});
    expect(result.code).toBe("unknown");
  });

  it("should classify Error('Internal Server Error') as server_error", (): void => {
    const result: ParsedAuthError = parseAuthError(new Error("Internal Server Error"));
    expect(result.code).toBe("server_error");
    expect(result.retryable).toBe(true);
  });

  it("should classify Error('Service Unavailable') as server_error", (): void => {
    const result: ParsedAuthError = parseAuthError(new Error("Service Unavailable"));
    expect(result.code).toBe("server_error");
  });

  it("should classify Error('Too many requests') as rate_limited", (): void => {
    const result: ParsedAuthError = parseAuthError(new Error("Too many requests"));
    expect(result.code).toBe("rate_limited");
  });

  it("should classify Error('Session expired') as session_expired", (): void => {
    const result: ParsedAuthError = parseAuthError(new Error("Session expired"));
    expect(result.code).toBe("session_expired");
  });

  it("should classify Error('Network issue') as network_error", (): void => {
    const result: ParsedAuthError = parseAuthError(new Error("Network issue"));
    expect(result.code).toBe("network_error");
  });

  it("should classify Error with unknown message as unknown", (): void => {
    const result: ParsedAuthError = parseAuthError(new Error("Something completely different"));
    expect(result.code).toBe("unknown");
  });
});

describe("parseAuthError retryable flag", (): void => {
  it("should mark network_error as retryable", (): void => {
    expect(parseAuthError(new TypeError("Failed to fetch")).retryable).toBe(true);
  });

  it("should mark server_error as retryable", (): void => {
    expect(parseAuthError({ status: 500 }).retryable).toBe(true);
  });

  it("should mark invalid_credentials as not retryable", (): void => {
    expect(parseAuthError(new Error("Unauthorized")).retryable).toBe(false);
  });

  it("should mark access_denied as not retryable", (): void => {
    expect(parseAuthError("access_denied").retryable).toBe(false);
  });

  it("should mark rate_limited as not retryable", (): void => {
    expect(parseAuthError("rate_limited").retryable).toBe(false);
  });

  it("should mark session_expired as not retryable", (): void => {
    expect(parseAuthError("session_expired").retryable).toBe(false);
  });

  it("should mark unknown as not retryable", (): void => {
    expect(parseAuthError(null).retryable).toBe(false);
  });
});

describe("getErrorMessage", (): void => {
  const allCodes: AuthErrorCode[] = [
    "access_denied",
    "invalid_credentials",
    "server_error",
    "network_error",
    "rate_limited",
    "session_expired",
    "unknown",
  ];

  it("should return the correct message for access_denied", (): void => {
    expect(getErrorMessage("access_denied")).toBe(
      "Authentication paused. Please try again when ready."
    );
  });

  it("should return the correct message for invalid_credentials", (): void => {
    expect(getErrorMessage("invalid_credentials")).toBe(
      "The email and password combination wasn't recognized."
    );
  });

  it("should return the correct message for server_error", (): void => {
    expect(getErrorMessage("server_error")).toBe(
      "The service is taking a break. Please try again in a moment."
    );
  });

  it("should return the correct message for network_error", (): void => {
    expect(getErrorMessage("network_error")).toBe(
      "Unable to connect. Check your network and try again."
    );
  });

  it("should return the correct message for rate_limited", (): void => {
    expect(getErrorMessage("rate_limited")).toBe(
      "You've tried a few times. Take a moment and try again shortly."
    );
  });

  it("should return the correct message for session_expired", (): void => {
    expect(getErrorMessage("session_expired")).toBe(
      "Your session ended. Please sign in again when ready."
    );
  });

  it("should return the correct message for unknown", (): void => {
    expect(getErrorMessage("unknown")).toBe(
      "Authentication didn't complete. Please try again when ready."
    );
  });

  it("should return a non-empty string for every error code", (): void => {
    for (const code of allCodes) {
      const message = getErrorMessage(code);
      expect(message).toBeTruthy();
      expect(message.length).toBeGreaterThan(0);
    }
  });
});

describe("PDA-friendly language compliance", (): void => {
  const allCodes: AuthErrorCode[] = [
    "access_denied",
    "invalid_credentials",
    "server_error",
    "network_error",
    "rate_limited",
    "session_expired",
    "unknown",
  ];

  it("should not contain any forbidden words in any message", (): void => {
    for (const code of allCodes) {
      const message = getErrorMessage(code).toLowerCase();
      for (const forbidden of FORBIDDEN_WORDS) {
        expect(message).not.toContain(forbidden);
      }
    }
  });

  it("should not contain forbidden words in parseAuthError output messages", (): void => {
    const testInputs: unknown[] = [
      new TypeError("Failed to fetch"),
      new Error("Unauthorized"),
      new Error("Internal Server Error"),
      { status: 429 },
      { status: 500 },
      "access_denied",
      "session_expired",
      null,
      undefined,
      42,
    ];

    for (const input of testInputs) {
      const result = parseAuthError(input);
      const message = result.message.toLowerCase();
      for (const forbidden of FORBIDDEN_WORDS) {
        expect(message).not.toContain(forbidden);
      }
    }
  });
});
