/**
 * Unit Tests: Date Handling and Storage
 *
 * Tests to prevent the "Jan 1, 1970" (Unix epoch) date bug
 * where invalid dates from localStorage caused "Missing required fields" errors
 *
 * Bug details:
 * - When dates were restored from localStorage with null/undefined/0 values
 * - new Date() would create January 1, 1970 (Unix epoch)
 * - This passed client validation but failed API validation
 * - Error message "Missing required fields" was confusing
 */

import {
  saveBookingDraft,
  restoreBookingDraft,
  clearBookingDraft,
  getBookingFromStorage,
  saveBookingToStorage,
} from "@/components/DemoScheduler/utils/storageHelpers";

// Mock localStorage
const localStorageMock = (() => {
  let store: Record<string, string> = {};
  return {
    getItem: jest.fn((key: string) => store[key] || null),
    setItem: jest.fn((key: string, value: string) => {
      store[key] = value;
    }),
    removeItem: jest.fn((key: string) => {
      delete store[key];
    }),
    clear: jest.fn(() => {
      store = {};
    }),
    get length() {
      return Object.keys(store).length;
    },
    key: jest.fn((index: number) => Object.keys(store)[index] || null),
  };
})();

Object.defineProperty(window, "localStorage", {
  value: localStorageMock,
});

describe("Date Handling - Epoch Date Prevention", () => {
  beforeEach(() => {
    localStorageMock.clear();
    jest.clearAllMocks();
  });

  describe("Invalid Date Detection", () => {
    it("should detect Unix epoch date (Jan 1, 1970) as invalid", () => {
      const epochDate = new Date(0);
      expect(epochDate.getFullYear()).toBe(1970);
      expect(epochDate.getMonth()).toBe(0); // January
      expect(epochDate.getDate()).toBe(1);
    });

    it("should detect invalid date from null", () => {
      const invalidDate = new Date(null as any);
      expect(isNaN(invalidDate.getTime())).toBe(false); // Actually creates epoch!
      expect(invalidDate.getFullYear()).toBe(1970);
    });

    it("should detect invalid date from undefined coerced to number", () => {
      const invalidDate = new Date(Number(undefined)); // NaN
      expect(isNaN(invalidDate.getTime())).toBe(true);
    });

    it("should detect invalid date from empty string", () => {
      const invalidDate = new Date("");
      expect(isNaN(invalidDate.getTime())).toBe(true);
    });

    it("should detect invalid date from garbage string", () => {
      const invalidDate = new Date("not-a-date");
      expect(isNaN(invalidDate.getTime())).toBe(true);
    });
  });

  describe("Storage Date Restoration", () => {
    it("should restore valid future date from storage", () => {
      const futureDate = new Date();
      futureDate.setDate(futureDate.getDate() + 7); // 7 days from now

      const state = {
        meetingDuration: 60,
        selectedDate: futureDate,
        selectedTime: "14:00",
      };

      saveBookingDraft(state as any);
      const restored = restoreBookingDraft();

      expect(restored).not.toBeNull();
      expect(restored?.selectedDate).toBeInstanceOf(Date);
      expect((restored?.selectedDate as Date).getFullYear()).toBeGreaterThan(
        1970,
      );
    });

    it("should NOT restore epoch date (Jan 1, 1970) from corrupted storage", () => {
      // Simulate corrupted storage with epoch date
      const corruptedState = {
        state: {
          meetingDuration: 60,
          selectedDate: "1970-01-01T00:00:00.000Z", // Epoch as ISO string
          selectedTime: "14:00",
        },
        timestamp: Date.now(),
        version: "1.0.0",
      };

      localStorageMock.setItem(
        "mawidi_booking_draft",
        JSON.stringify(corruptedState),
      );

      const restored = restoreBookingDraft();

      // Should either return null or have undefined date
      if (restored) {
        expect(restored.selectedDate).toBeUndefined();
      }
    });

    it("should NOT restore null date value", () => {
      const corruptedState = {
        state: {
          meetingDuration: 60,
          selectedDate: null,
          selectedTime: "14:00",
        },
        timestamp: Date.now(),
        version: "1.0.0",
      };

      localStorageMock.setItem(
        "mawidi_booking_draft",
        JSON.stringify(corruptedState),
      );

      const restored = restoreBookingDraft();

      if (restored) {
        // null or undefined are both acceptable for invalid dates
        expect(restored.selectedDate == null).toBe(true);
      }
    });

    it("should NOT restore date with year 0", () => {
      const corruptedState = {
        state: {
          meetingDuration: 60,
          selectedDate: "0000-01-01T00:00:00.000Z",
          selectedTime: "14:00",
        },
        timestamp: Date.now(),
        version: "1.0.0",
      };

      localStorageMock.setItem(
        "mawidi_booking_draft",
        JSON.stringify(corruptedState),
      );

      const restored = restoreBookingDraft();

      if (restored) {
        expect(restored.selectedDate).toBeUndefined();
      }
    });

    it("should NOT restore invalid ISO date string", () => {
      const corruptedState = {
        state: {
          meetingDuration: 60,
          selectedDate: "invalid-date-string",
          selectedTime: "14:00",
        },
        timestamp: Date.now(),
        version: "1.0.0",
      };

      localStorageMock.setItem(
        "mawidi_booking_draft",
        JSON.stringify(corruptedState),
      );

      const restored = restoreBookingDraft();

      if (restored) {
        expect(restored.selectedDate).toBeUndefined();
      }
    });

    it("should NOT restore timestamp 0 (epoch)", () => {
      const corruptedState = {
        state: {
          meetingDuration: 60,
          selectedDate: 0, // Epoch timestamp
          selectedTime: "14:00",
        },
        timestamp: Date.now(),
        version: "1.0.0",
      };

      localStorageMock.setItem(
        "mawidi_booking_draft",
        JSON.stringify(corruptedState),
      );

      const restored = restoreBookingDraft();

      if (restored) {
        // Either undefined, null, or 0 (falsy) means invalid - all are acceptable
        // The key is that it should NOT be a valid Date object representing epoch
        const dateVal = restored.selectedDate;
        if (dateVal instanceof Date) {
          // If it's a Date, it should NOT be valid epoch (year > 1970)
          expect(dateVal.getFullYear()).toBeGreaterThan(1970);
        } else {
          // If not a Date, it should be falsy (null, undefined, 0)
          expect(!dateVal).toBe(true);
        }
      }
    });
  });

  describe("Edge Cases", () => {
    it("should handle negative timestamp (dates before 1970)", () => {
      const preEpochDate = new Date(-86400000); // Dec 31, 1969
      expect(preEpochDate.getFullYear()).toBe(1969);

      const state = {
        state: {
          meetingDuration: 60,
          selectedDate: preEpochDate.toISOString(),
          selectedTime: "14:00",
        },
        timestamp: Date.now(),
        version: "1.0.0",
      };

      localStorageMock.setItem("mawidi_booking_draft", JSON.stringify(state));

      const restored = restoreBookingDraft();

      // Should not restore pre-1970 dates
      if (restored) {
        expect(restored.selectedDate).toBeUndefined();
      }
    });

    it("should handle date exactly at epoch boundary", () => {
      const epochBoundary = new Date("1970-01-01T00:00:00.001Z"); // 1ms after epoch

      const state = {
        state: {
          meetingDuration: 60,
          selectedDate: epochBoundary.toISOString(),
          selectedTime: "14:00",
        },
        timestamp: Date.now(),
        version: "1.0.0",
      };

      localStorageMock.setItem("mawidi_booking_draft", JSON.stringify(state));

      const restored = restoreBookingDraft();

      // 1970 dates should not be restored (booking window is future dates only)
      if (restored) {
        expect(restored.selectedDate).toBeUndefined();
      }
    });

    it("should handle far future dates", () => {
      const futureDate = new Date("2099-12-31T23:59:59.999Z");

      const state = {
        meetingDuration: 60,
        selectedDate: futureDate,
        selectedTime: "14:00",
      };

      saveBookingDraft(state as any);
      const restored = restoreBookingDraft();

      // Very far future dates should be preserved (validation is elsewhere)
      expect(restored).not.toBeNull();
      if (restored?.selectedDate) {
        // Date may shift slightly due to timezone, check it's around 2099-2100
        const year = (restored.selectedDate as Date).getFullYear();
        expect(year).toBeGreaterThanOrEqual(2099);
        expect(year).toBeLessThanOrEqual(2100);
      }
    });
  });

  describe("Storage Persistence", () => {
    it("should save and restore complete booking state", () => {
      const validDate = new Date();
      validDate.setDate(validDate.getDate() + 14); // 2 weeks from now

      const state = {
        meetingDuration: 60,
        selectedDate: validDate,
        selectedTime: "15:30",
        timezone: "Asia/Qatar",
        userInfo: {
          name: "Test User",
          email: "test@example.com",
          phone: "+97412345678",
          message: "Test booking message",
        },
      };

      const saved = saveBookingDraft(state as any);
      expect(saved).toBe(true);

      const restored = restoreBookingDraft();
      expect(restored).not.toBeNull();
      expect(restored?.meetingDuration).toBe(60);
      expect(restored?.selectedTime).toBe("15:30");
      expect(restored?.selectedDate).toBeInstanceOf(Date);

      // Verify date is correct
      const restoredDate = restored?.selectedDate as Date;
      expect(restoredDate.getDate()).toBe(validDate.getDate());
      expect(restoredDate.getMonth()).toBe(validDate.getMonth());
      expect(restoredDate.getFullYear()).toBe(validDate.getFullYear());
    });

    it("should clear storage completely", () => {
      const state = {
        meetingDuration: 30,
        selectedDate: new Date(),
        selectedTime: "10:00",
      };

      saveBookingDraft(state as any);
      expect(restoreBookingDraft()).not.toBeNull();

      clearBookingDraft();
      expect(restoreBookingDraft()).toBeNull();
    });
  });
});

describe("Date Validation Helper", () => {
  /**
   * Helper function to validate dates (mirrors the validation in useBookingForm)
   */
  function isValidBookingDate(date: unknown): boolean {
    if (!date) return false;

    const dateObj =
      date instanceof Date ? date : new Date(date as string | number);

    // Check if date is valid (not NaN)
    if (isNaN(dateObj.getTime())) return false;

    // Check if date is not epoch (year must be > 1970)
    if (dateObj.getFullYear() <= 1970) return false;

    // Check if date is in the future
    const now = new Date();
    now.setHours(0, 0, 0, 0);
    if (dateObj < now) return false;

    return true;
  }

  it("should accept valid future date", () => {
    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + 7);
    expect(isValidBookingDate(futureDate)).toBe(true);
  });

  it("should reject null", () => {
    expect(isValidBookingDate(null)).toBe(false);
  });

  it("should reject undefined", () => {
    expect(isValidBookingDate(undefined)).toBe(false);
  });

  it("should reject epoch date", () => {
    expect(isValidBookingDate(new Date(0))).toBe(false);
  });

  it("should reject 1970 dates", () => {
    expect(isValidBookingDate(new Date("1970-06-15"))).toBe(false);
  });

  it("should reject past dates", () => {
    const pastDate = new Date();
    pastDate.setDate(pastDate.getDate() - 1);
    expect(isValidBookingDate(pastDate)).toBe(false);
  });

  it("should reject invalid ISO string", () => {
    expect(isValidBookingDate("not-a-date")).toBe(false);
  });

  it("should accept valid ISO string", () => {
    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + 7);
    expect(isValidBookingDate(futureDate.toISOString())).toBe(true);
  });

  it("should accept valid timestamp", () => {
    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + 7);
    expect(isValidBookingDate(futureDate.getTime())).toBe(true);
  });

  it("should reject timestamp 0", () => {
    expect(isValidBookingDate(0)).toBe(false);
  });
});

describe("API Date Format", () => {
  /**
   * Helper to format date for API (mirrors useBookingForm.submitBooking)
   */
  function formatDateForAPI(date: Date): string {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
  }

  it("should format date correctly for API", () => {
    const date = new Date(2025, 11, 15); // Dec 15, 2025
    expect(formatDateForAPI(date)).toBe("2025-12-15");
  });

  it("should pad single-digit months", () => {
    const date = new Date(2025, 0, 15); // Jan 15, 2025
    expect(formatDateForAPI(date)).toBe("2025-01-15");
  });

  it("should pad single-digit days", () => {
    const date = new Date(2025, 11, 5); // Dec 5, 2025
    expect(formatDateForAPI(date)).toBe("2025-12-05");
  });

  it("should NOT produce 1970 date from epoch", () => {
    const epochDate = new Date(0);
    const formatted = formatDateForAPI(epochDate);
    expect(formatted).toBe("1970-01-01");
    // This test documents the bug - epoch dates would produce 1970-01-01
    // The fix prevents epoch dates from reaching this function
  });
});
