import { assertEquals, assert } from "jsr:@std/assert";
import { resolveEventStart, expandRecurrences, dateToTimezoneString } from "../icalendar.ts";

const TEST_NOW = new Date("2026-01-20T12:00:00Z");

Deno.test("Variation: Standard Opaque Meeting (Busy)", async () => {
  const icsEvent = {
    summary: "Discuss Alletra MP terraform provider requirements",
    start: {
      date: "2026-01-16T15:30:00.000",
      local: {
        date: "2026-01-16T15:30:00.000",
        timezone: "GMT Standard Time"
      }
    },
    transp: "OPAQUE",
    "x-microsoft-cdo-busystatus": "BUSY"
  };
  
  const result = await resolveEventStart(icsEvent);
  // GMT Standard Time in Jan is UTC+0
  assertEquals(result?.toISOString(), "2026-01-16T15:30:00.000Z");
});

Deno.test("Variation: Transparent Meeting (Free)", async () => {
  const icsEvent = {
    summary: "Following: Neutron Star Program Meeting",
    start: {
      date: "2026-01-20T08:30:00.000",
      local: {
        date: "2026-01-20T08:30:00.000",
        timezone: "Pacific Standard Time"
      }
    },
    transp: "TRANSPARENT",
    "x-microsoft-cdo-busystatus": "FREE"
  };
  
  const result = await resolveEventStart(icsEvent);
  // PST in Jan is UTC-8
  assertEquals(result?.toISOString(), "2026-01-20T16:30:00.000Z");
});

Deno.test("Variation: Recurring Weekly (Multi-day: MO,TU,WE,TH,FR)", () => {
  const start = new Date("2026-01-16T13:00:00");
  const icsEvent = {
    summary: "BUSY Weekly",
    start: start.toISOString(),
    rrule: "FREQ=WEEKLY;UNTIL=20260814T170000Z;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR;WKST=SU"
  };
  
  // Use TEST_NOW to ensure the window matches
  const results = expandRecurrences(icsEvent, 7, "UTC", TEST_NOW); 
  // Should have multiple occurrences per week
  assert(results.length > 1);
  assert(results.some(r => r.startLocal.includes("2026-01-19"))); // Monday
  assert(results.some(r => r.startLocal.includes("2026-01-20"))); // Tuesday
});

Deno.test("Variation: Recurring with WORKWEEKSTART (Outlook style)", () => {
  const start = new Date("2026-01-20T08:30:00");
  const icsEvent = {
    summary: "Outlook Style Meeting",
    start: start.toISOString(),
    rrule: {
        frequency: "WEEKLY",
        interval: 1,
        byday: "TU",
        workweekstart: "MO"
    }
  };
  
  const results = expandRecurrences(icsEvent, 30, "UTC", TEST_NOW);
  assert(results.length > 0);
  assert(results[0].startLocal.includes("2026-01-20"));
});

Deno.test("Variation: Recurring with EXDATE (Exclusion)", () => {
  const start = new Date("2026-01-20T08:30:00");
  const icsEvent = {
    summary: "HPE-Veeam check-in",
    start: start.toISOString(),
    rrule: "FREQ=WEEKLY;UNTIL=20260324T143000Z;INTERVAL=1;BYDAY=TU;WKST=SU",
    exdate: ["2026-02-03T08:30:00"]
  };
  
  const results = expandRecurrences(icsEvent, 60, "UTC", TEST_NOW);
  const dates = results.map(r => r.startLocal);
  assert(dates.some(d => d.includes("2026-01-20T")), "Should find first occurrence");
  assert(dates.some(d => d.includes("2026-01-27T")), "Should find second occurrence");
  assert(!dates.some(d => d.includes("2026-02-03T")), "EXDATE 2026-02-03 should be excluded");
  assert(dates.some(d => d.includes("2026-02-10T")), "Should find fourth occurrence");
});

Deno.test("Variation: Monthly Recurring (Last Friday)", () => {
  const start = new Date("2026-01-30T10:00:00");
  const icsEvent = {
    summary: "Monthly Planning",
    start: start.toISOString(), // This is the last Friday of Jan 2026
    rrule: "FREQ=MONTHLY;UNTIL=20260731T170000Z;INTERVAL=1;BYDAY=-1FR"
  };
  
  const results = expandRecurrences(icsEvent, 100, "UTC", TEST_NOW);
  const dates = results.map(r => r.startLocal);
  
  assert(dates.some(d => d.includes("2026-01-30")), "Should find Jan occurrence");
  assert(dates.some(d => d.includes("2026-02-27")), "Should find Feb occurrence"); 
  assert(dates.some(d => d.includes("2026-03-27")), "Should find Mar occurrence");
});

Deno.test("Variation: Tentative Meeting", async () => {
    const icsEvent = {
      summary: "CO SW&P: Morpheus SW Core Team",
      start: {
        date: "2026-01-19T11:30:00.000",
        local: {
          date: "2026-01-19T11:30:00.000",
          timezone: "Central Standard Time"
        }
      },
      "x-microsoft-cdo-busystatus": "TENTATIVE"
    };
    
    const result = await resolveEventStart(icsEvent);
    // CST in Jan is UTC-6
    assertEquals(result?.toISOString(), "2026-01-19T17:30:00.000Z");
});

Deno.test("Variation: Long Location/URL", () => {
    const icsEvent = {
        summary: "Omnissa Horizon:HPE VME Weekly Cadence",
        location: "https://omnissa.zoom.us/j/84780526943?pwd=fow88EiiZyUKsW26JrJavqiirbb1hv.1&from=addon"
    };
    assertEquals(icsEvent.location.length > 50, true);
});

Deno.test("Feature: Unlimited lookback window", () => {
    const start = new Date(TEST_NOW.getTime() - 500 * 86400000); // 500 days ago
    const icsEvent = {
        summary: "Event from 500 days ago",
        start: start.toISOString(),
        rrule: "FREQ=DAILY;COUNT=1000"
    };
    
    const results = expandRecurrences(icsEvent, 30, "UTC", TEST_NOW);
    // Should include events from 500 days ago because there is now no limit
    assert(results.some(r => r.startLocal === dateToTimezoneString(start, "UTC")), "Should find occurrence from 500 days ago");
});

Deno.test("Feature: Hash Collision Prevention (Same UID/Start, Different Summary)", async () => {
    // This happens in reachcalendar.ics where a "Following:" event shares UID/Time with main event
    const event1 = {
        start: "2026-01-20T08:30:00",
        uid: "collision-uid",
        summary: "Main Meeting"
    };
    const event2 = {
        start: "2026-01-20T08:30:00",
        uid: "collision-uid",
        summary: "Following: Main Meeting"
    };
    
    const hash1 = await sha256Hash(`${event1.start}${event1.uid}${event1.summary}`);
    const hash2 = await sha256Hash(`${event2.start}${event2.uid}${event2.summary}`);
    
    assert(hash1 !== hash2, "Hashes must be unique even if UID and Start match");
});

// Helper needed for the test above since it's not exported from icalendar.ts
async function sha256Hash(str: string): Promise<string> {
  const encoder = new TextEncoder();
  const data = encoder.encode(str);
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  return hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
}
