/// <reference no-default-lib="true" />
/// <reference lib="es2020" />
/// <reference lib="webworker" />

declare const self: ServiceWorkerGlobalScope;
export {};

// Notification tag prefix for VibeTunnel notifications
const NOTIFICATION_TAG_PREFIX = 'vibetunnel-';

// Types for push notification payloads
interface SessionExitData {
  type: 'session-exit';
  sessionId: string;
  sessionName?: string;
  command?: string;
  exitCode: number;
  duration?: number;
  timestamp: number;
}

interface SessionStartData {
  type: 'session-start';
  sessionId: string;
  sessionName?: string;
  command?: string;
  timestamp: number;
}

interface SessionErrorData {
  type: 'session-error';
  sessionId: string;
  sessionName?: string;
  command?: string;
  error: string;
  timestamp: number;
}

interface SystemAlertData {
  type: 'system-alert';
  message: string;
  level: 'info' | 'warning' | 'error';
  timestamp: number;
}

type NotificationData = SessionExitData | SessionStartData | SessionErrorData | SystemAlertData;

interface PushNotificationPayload {
  title: string;
  body: string;
  icon?: string;
  badge?: string;
  data: NotificationData;
  actions?: Array<{
    action: string;
    title: string;
    icon?: string;
  }>;
  tag?: string;
  requireInteraction?: boolean;
}

// Install event
self.addEventListener('install', (_event: ExtendableEvent) => {
  console.log('[SW] Installing service worker');

  // Force activation of new service worker
  self.skipWaiting();
});

// Activate event
self.addEventListener('activate', (event: ExtendableEvent) => {
  console.log('[SW] Activating service worker');

  event.waitUntil(
    // Take control of all pages
    self.clients.claim()
  );
});

// Push event - handle incoming push notifications
self.addEventListener('push', (event: PushEvent) => {
  console.log('[SW] Push event received');

  if (!event.data) {
    console.warn('[SW] Push event has no data');
    return;
  }

  let payload: PushNotificationPayload;

  try {
    payload = event.data.json();
  } catch (error) {
    console.error('[SW] Failed to parse push payload:', error);
    return;
  }

  event.waitUntil(handlePushNotification(payload));
});

// Notification click event - handle user interactions
self.addEventListener('notificationclick', (event: NotificationEvent) => {
  console.log('[SW] Notification clicked:', event.notification.tag);

  event.notification.close();

  const data = event.notification.data as NotificationData;

  event.waitUntil(handleNotificationClick(event.action, data));
});

// Notification close event - track dismissals
self.addEventListener('notificationclose', (event: NotificationEvent) => {
  console.log('[SW] Notification closed:', event.notification.tag);

  const data = event.notification.data as NotificationData;

  // Optional: Send analytics or cleanup
  if (data.type === 'session-exit' || data.type === 'session-error') {
    // Could track notification dismissal metrics
  }
});

// No background sync needed

async function handlePushNotification(payload: PushNotificationPayload): Promise<void> {
  const { title, body, icon, badge, data, actions, tag, requireInteraction } = payload;

  try {
    // Create notification options
    const notificationOptions: NotificationOptions = {
      body,
      icon: icon || '/apple-touch-icon.png',
      badge: badge || '/favicon-32.png',
      data,
      tag: tag || `${NOTIFICATION_TAG_PREFIX}${data.type}-${Date.now()}`,
      requireInteraction: requireInteraction || data.type === 'session-error',
      silent: false,
      // @ts-expect-error - renotify is a valid option but not in TypeScript types
      renotify: true,
      actions: actions || getDefaultActions(data),
      timestamp: data.timestamp,
    };

    // Add vibration pattern for mobile devices
    if ('vibrate' in navigator) {
      // @ts-expect-error - vibrate is a valid option but not in TypeScript types
      notificationOptions.vibrate = getVibrationPattern(data.type);
    }

    // Show the notification
    await self.registration.showNotification(title, notificationOptions);

    console.log('[SW] Notification shown:', title);
  } catch (error) {
    console.error('[SW] Failed to show notification:', error);
  }
}

interface NotificationAction {
  action: string;
  title: string;
}

function getDefaultActions(data: NotificationData): NotificationAction[] {
  const baseActions: NotificationAction[] = [
    {
      action: 'dismiss',
      title: 'Dismiss',
    },
  ];

  switch (data.type) {
    case 'session-exit':
    case 'session-error':
    case 'session-start': {
      return [
        {
          action: 'view-session',
          title: 'View Session',
        },
        ...baseActions,
      ];
    }
    case 'system-alert': {
      return [
        {
          action: 'view-logs',
          title: 'View Logs',
        },
        ...baseActions,
      ];
    }
    default:
      return baseActions;
  }
}

function getVibrationPattern(notificationType: string): number[] {
  switch (notificationType) {
    case 'session-error':
      return [200, 100, 200, 100, 200]; // Urgent pattern
    case 'session-exit':
      return [100, 50, 100]; // Short notification
    case 'session-start':
      return [50]; // Very brief
    case 'system-alert':
      return [150, 75, 150]; // Moderate pattern
    default:
      return [100]; // Default brief vibration
  }
}

async function handleNotificationClick(action: string, data: NotificationData): Promise<void> {
  const clients = await self.clients.matchAll({
    type: 'window',
    includeUncontrolled: true,
  });

  // Try to focus existing window first
  for (const client of clients) {
    if (client.url.includes(self.location.origin)) {
      try {
        await client.focus();

        // Send action to the client
        client.postMessage({
          type: 'notification-action',
          action,
          data,
        });

        return;
      } catch (error) {
        console.warn('[SW] Failed to focus client:', error);
      }
    }
  }

  // No existing window, open a new one
  let url = self.location.origin;

  switch (action) {
    case 'view-session': {
      if (
        data.type === 'session-exit' ||
        data.type === 'session-error' ||
        data.type === 'session-start'
      ) {
        url += `/?session=${data.sessionId}`;
      }
      break;
    }
    case 'view-logs': {
      url += '/logs';
      break;
    }
    default:
      // Just open the main page
      break;
  }

  try {
    await self.clients.openWindow(url);
  } catch (error) {
    console.error('[SW] Failed to open window:', error);
  }
}

// No offline notification handling needed

// No fetch event handler needed - we don't cache anything

// Message handler for communication with main thread
self.addEventListener('message', (event: ExtendableMessageEvent) => {
  const { data } = event;

  switch (data.type) {
    case 'CLEAR_NOTIFICATIONS': {
      // Clear all VibeTunnel notifications
      clearAllNotifications();
      break;
    }
    case 'SKIP_WAITING': {
      self.skipWaiting();
      break;
    }
  }
});

// No queueing needed

async function clearAllNotifications(): Promise<void> {
  try {
    const notifications = await self.registration.getNotifications();

    for (const notification of notifications) {
      if (notification.tag?.startsWith(NOTIFICATION_TAG_PREFIX)) {
        notification.close();
      }
    }

    console.log('[SW] Cleared all VibeTunnel notifications');
  } catch (error) {
    console.error('[SW] Failed to clear notifications:', error);
  }
}

console.log('[SW] Service worker loaded');
