import { google, type calendar_v3 } from "googleapis";
import { readFileSync, writeFileSync, existsSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { upsertGoogleCachedEvents, clearGoogleCachedEventsForCalendar } from "./db.js";

const NOTIFICATIONS_URL = process.env.NOTIFICATIONS_SERVICE_URL || "http://127.0.0.1:8087";

function notifyCalendarError(title: string, description: string): void {
  fetch(`${NOTIFICATIONS_URL}/notifications`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ title, description, source: "calendar", level: "error" }),
  }).catch(console.error);
}

const __dirname = dirname(fileURLToPath(import.meta.url));

// Paths must be supplied via environment variables (set during module installation)
const CREDENTIALS_PATH = process.env.GOOGLE_CREDENTIALS_PATH || "";
const TOKEN_PATH = process.env.GOOGLE_TOKEN_PATH || "";

interface GoogleCredentials {
  client_id: string;
  client_secret: string;
  redirect_uri: string;
}

interface GoogleToken {
  access_token: string;
  refresh_token: string;
  token_type: string;
  expiry_date: number;
}

let calendarClient: calendar_v3.Calendar | null = null;

function getAuth() {
  if (!CREDENTIALS_PATH || !TOKEN_PATH) {
    console.warn("Google Calendar credentials paths not configured — Google integration disabled");
    return null;
  }
  if (!existsSync(CREDENTIALS_PATH) || !existsSync(TOKEN_PATH)) {
    console.warn("Google Calendar credentials not found — Google integration disabled");
    return null;
  }

  const credentials: GoogleCredentials = JSON.parse(readFileSync(CREDENTIALS_PATH, "utf8"));
  const token: GoogleToken = JSON.parse(readFileSync(TOKEN_PATH, "utf8"));

  const oauth2 = new google.auth.OAuth2(
    credentials.client_id,
    credentials.client_secret,
    credentials.redirect_uri
  );
  oauth2.setCredentials(token);

  oauth2.on("tokens", (newTokens) => {
    const merged = { ...token, ...newTokens };
    writeFileSync(TOKEN_PATH, JSON.stringify(merged, null, 2));
  });

  return oauth2;
}

function getCalendarClient(): calendar_v3.Calendar | null {
  if (calendarClient) return calendarClient;
  const auth = getAuth();
  if (!auth) return null;
  calendarClient = google.calendar({ version: "v3", auth });
  return calendarClient;
}

export async function fetchGoogleCalendarEvents(
  calendarId: string,
  calendarName: string,
  personEmail: string,
  timeMin: string,
  timeMax: string
): Promise<void> {
  const client = getCalendarClient();
  if (!client) return;

  try {
    const res = await client.events.list({
      calendarId,
      timeMin,
      timeMax,
      singleEvents: true,
      orderBy: "startTime",
      maxResults: 250,
    });

    const events = (res.data.items || []).map((item) => ({
      google_calendar_id: calendarId,
      google_calendar_name: calendarName,
      google_event_id: item.id!,
      title: item.summary || "(No title)",
      description: item.description || null,
      start_time: item.start?.dateTime || `${item.start?.date}T00:00:00Z`,
      end_time: item.end?.dateTime || `${item.end?.date}T23:59:59Z`,
      all_day: item.start?.date ? 1 : 0,
      location: item.location || null,
      person_email: personEmail,
    }));

    clearGoogleCachedEventsForCalendar(calendarId);
    if (events.length > 0) {
      upsertGoogleCachedEvents(events);
    }

    console.log(`Synced ${events.length} events from ${calendarName} (${personEmail})`);
  } catch (e) {
    console.error(`Failed to sync Google Calendar ${calendarId}:`, e);
    const message = e instanceof Error ? e.message : String(e);
    notifyCalendarError(
      `Google Calendar Sync Failed: ${calendarName}`,
      `Failed to sync ${calendarName} (${personEmail}): ${message}`
    );
  }
}

export async function createGoogleCalendarEvent(params: {
  summary: string;
  description?: string;
  startTime: string;
  endTime: string;
  allDay: boolean;
  location?: string;
  attendeeEmails: string[];
}): Promise<string | null> {
  const client = getCalendarClient();
  if (!client) return null;

  try {
    const event: calendar_v3.Schema$Event = {
      summary: params.summary,
      description: params.description,
      location: params.location,
      start: params.allDay
        ? { date: params.startTime.split("T")[0] }
        : { dateTime: params.startTime, timeZone: "America/New_York" },
      end: params.allDay
        ? { date: params.endTime.split("T")[0] }
        : { dateTime: params.endTime, timeZone: "America/New_York" },
      attendees: params.attendeeEmails.map((email) => ({ email })),
    };

    const res = await client.events.insert({
      calendarId: "primary",
      requestBody: event,
      sendUpdates: "all",
    });

    console.log(`Created Google Calendar event: ${res.data.id}`);
    return res.data.id || null;
  } catch (e) {
    console.error("Failed to create Google Calendar event:", e);
    return null;
  }
}

export async function listSharedCalendars(): Promise<
  { id: string; name: string; primary: boolean }[]
> {
  const client = getCalendarClient();
  if (!client) return [];

  try {
    const res = await client.calendarList.list();
    return (res.data.items || []).map((item) => ({
      id: item.id!,
      name: item.summary || item.id!,
      primary: item.primary || false,
    }));
  } catch (e) {
    console.error("Failed to list Google Calendars:", e);
    return [];
  }
}

// Periodic sync
const SYNC_INTERVAL = 15 * 60 * 1000; // 15 minutes
let syncIntervalId: ReturnType<typeof setInterval> | null = null;

const SYNC_CONFIG_PATH = join(__dirname, "..", "data", "google-sync-config.json");

interface SyncConfig {
  calendars: {
    google_calendar_id: string;
    name: string;
    person_email: string;
  }[];
}

function loadSyncConfig(): SyncConfig {
  if (!existsSync(SYNC_CONFIG_PATH)) return { calendars: [] };
  return JSON.parse(readFileSync(SYNC_CONFIG_PATH, "utf8"));
}

export { loadSyncConfig };

export function saveSyncConfig(config: SyncConfig): void {
  writeFileSync(SYNC_CONFIG_PATH, JSON.stringify(config, null, 2));
}

async function syncAllCalendars(): Promise<void> {
  const config = loadSyncConfig();
  if (config.calendars.length === 0) return;

  const now = new Date();
  const timeMin = new Date(now.getFullYear(), now.getMonth() - 1, 1).toISOString();
  const timeMax = new Date(now.getFullYear(), now.getMonth() + 3, 0).toISOString();

  for (const cal of config.calendars) {
    await fetchGoogleCalendarEvents(
      cal.google_calendar_id,
      cal.name,
      cal.person_email,
      timeMin,
      timeMax
    );
  }
}

export function startGoogleSync(): void {
  console.log("Google Calendar sync started (every 15 minutes)");
  syncAllCalendars().catch(console.error);
  syncIntervalId = setInterval(() => {
    syncAllCalendars().catch(console.error);
  }, SYNC_INTERVAL);
}

export function stopGoogleSync(): void {
  if (syncIntervalId) {
    clearInterval(syncIntervalId);
    syncIntervalId = null;
  }
}
