import { getDeviceId } from '../lib/device';

export type GuestNotificationCta = {
  label: string;
  href: string;
};

export type GuestNotificationItem = {
  id: number;
  type: string;
  title: string;
  body: string | null;
  status: 'new' | 'read' | 'dismissed';
  createdAt: string;
  readAt?: string | null;
  dismissedAt?: string | null;
  cta?: GuestNotificationCta | null;
  payload?: Record<string, unknown> | null;
};

export type GuestNotificationFetchResult = {
  notifications: GuestNotificationItem[];
  unreadCount: number;
  etag: string | null;
  notModified: boolean;
};

type GuestNotificationResponse = {
  data?: Array<{
    id?: number | string;
    type?: string;
    title?: string;
    body?: string | null;
    status?: 'new' | 'read' | 'dismissed';
    created_at?: string;
    read_at?: string | null;
    dismissed_at?: string | null;
    cta?: GuestNotificationCta | null;
    payload?: Record<string, unknown> | null;
  }>;
  meta?: {
    unread_count?: number;
  };
};

type GuestNotificationRow = NonNullable<GuestNotificationResponse['data']>[number];

function buildHeaders(etag?: string | null): HeadersInit {
  const headers: Record<string, string> = {
    Accept: 'application/json',
    'Content-Type': 'application/json',
    'X-Device-Id': getDeviceId(),
  };

  if (etag) {
    headers['If-None-Match'] = etag;
  }

  return headers;
}

function mapNotification(payload: GuestNotificationRow): GuestNotificationItem {
  return {
    id: Number(payload.id ?? 0),
    type: payload.type ?? 'broadcast',
    title: payload.title ?? '',
    body: payload.body ?? null,
    status: payload.status === 'read' || payload.status === 'dismissed' ? payload.status : 'new',
    createdAt: payload.created_at ?? new Date().toISOString(),
    readAt: payload.read_at ?? null,
    dismissedAt: payload.dismissed_at ?? null,
    cta: payload.cta ?? null,
    payload: payload.payload ?? null,
  };
}

export async function fetchGuestNotifications(
  eventToken: string,
  etag?: string | null,
  options?: { status?: 'unread' | 'read' | 'dismissed'; scope?: 'all' | 'uploads' | 'tips' | 'general' }
): Promise<GuestNotificationFetchResult> {
  const params = new URLSearchParams();
  if (options?.status) params.set('status', options.status);
  if (options?.scope && options.scope !== 'all') params.set('scope', options.scope);

  const response = await fetch(`/api/v1/events/${encodeURIComponent(eventToken)}/notifications${params.toString() ? `?${params.toString()}` : ''}`, {
    method: 'GET',
    headers: buildHeaders(etag),
    credentials: 'include',
  });

  if (response.status === 304 && etag) {
    return {
      notifications: [],
      unreadCount: 0,
      etag,
      notModified: true,
    };
  }

  if (!response.ok) {
    const reason = await safeParseError(response);
    throw new Error(reason ?? 'Benachrichtigungen konnten nicht geladen werden.');
  }

  const body = (await response.json()) as GuestNotificationResponse;
  const rows = Array.isArray(body.data) ? body.data : [];
  const notifications = rows.map(mapNotification);
  const unreadCount = typeof body.meta?.unread_count === 'number'
    ? body.meta.unread_count
    : notifications.filter((item) => item.status === 'new').length;

  return {
    notifications,
    unreadCount,
    etag: response.headers.get('ETag'),
    notModified: false,
  };
}

export async function markGuestNotificationRead(eventToken: string, notificationId: number): Promise<void> {
  await postNotificationAction(eventToken, notificationId, 'read');
}

export async function dismissGuestNotification(eventToken: string, notificationId: number): Promise<void> {
  await postNotificationAction(eventToken, notificationId, 'dismiss');
}

async function postNotificationAction(eventToken: string, notificationId: number, action: 'read' | 'dismiss'): Promise<void> {
  const response = await fetch(`/api/v1/events/${encodeURIComponent(eventToken)}/notifications/${notificationId}/${action}`, {
    method: 'POST',
    headers: buildHeaders(),
    credentials: 'include',
  });

  if (!response.ok) {
    const reason = await safeParseError(response);
    throw new Error(reason ?? 'Aktion konnte nicht ausgeführt werden.');
  }
}

async function safeParseError(response: Response): Promise<string | null> {
  try {
    const payload = await response.clone().json();
    const message = payload?.error?.message ?? payload?.message;
    if (typeof message === 'string' && message.trim() !== '') {
      return message.trim();
    }
  } catch (error) {
    console.warn('Failed to parse notification API error', error);
  }

  return null;
}
