import { Platform } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Reminder, reminderToMinutes, parseReminders } from '@/types';

const ID_MAP_KEY = 'notifications:taskIdToNotificationIds';

async function readIdMap(): Promise<Record<string, string[]>> {
  try {
    const raw = await AsyncStorage.getItem(ID_MAP_KEY);
    return raw ? (JSON.parse(raw) as Record<string, string[]>) : {};
  } catch {
    return {};
  }
}

async function writeIdMap(map: Record<string, string[]>): Promise<void> {
  try {
    await AsyncStorage.setItem(ID_MAP_KEY, JSON.stringify(map));
  } catch {
    // ignore
  }
}

function getNotificationsModule(): any {
  if (Platform.OS === 'web') return null;
  try {
    return import('expo-notifications');
  } catch {
    return null;
  }
}

export async function scheduleTaskReminder(task: any): Promise<void> {
  const notifications = await getNotificationsModule();
  const dueDate = task.dueDate ? new Date(task.dueDate) : null;

  if (!notifications) return;

  const idMap = await readIdMap();

  // Cancel any existing notifications for this task
  const oldIds = idMap[task.id] || [];
  for (const id of oldIds) {
    try {
      await notifications.cancelScheduledNotificationAsync(id);
    } catch {
      // ignore
    }
  }
  delete idMap[task.id];
  await writeIdMap(idMap);

  // Determine the set of reminders to schedule
  const reminders: Reminder[] = task.reminders
    ? parseReminders(task.reminders)
    : task.reminder && task.reminder !== 'none'
      ? [task.reminder as Reminder]
      : [];

  if (reminders.length === 0 || !dueDate || !task.dueTime) return;

  const granted = await requestNotificationPermission();
  if (!granted) return;

  const [hours, mins] = (task.dueTime as string).split(':').map(Number);
  if (!Number.isNaN(hours)) {
    dueDate.setHours(hours, Number.isNaN(mins) ? 0 : mins, 0, 0);
  }

  const newIds: string[] = [];

  for (const reminder of reminders) {
    const minutes = reminderToMinutes(reminder);
    if (minutes === null) continue;

    const triggerDate = new Date(dueDate.getTime() - minutes * 60000);
    if (triggerDate.getTime() <= Date.now()) continue;

    try {
      const id = await notifications.scheduleNotificationAsync({
        content: {
          title: task.title,
          body: `Due ${task.dueTime}${task.repeat && task.repeat !== 'none' ? ' · repeating' : ''}`,
          sound: 'default',
        },
        trigger: { type: 'date', date: triggerDate },
      });
      newIds.push(id);
    } catch (error) {
      if (__DEV__) console.warn('Failed to schedule reminder:', error);
    }
  }

  if (newIds.length > 0) {
    idMap[task.id] = newIds;
    await writeIdMap(idMap);
  }
}

export async function cancelTaskReminder(taskId: string): Promise<void> {
  const notifications = await getNotificationsModule();
  if (!notifications) return;

  const idMap = await readIdMap();
  const ids = idMap[taskId];
  if (!ids) return;

  for (const id of ids) {
    try {
      await notifications.cancelScheduledNotificationAsync(id);
    } catch {
      // ignore
    }
  }
  delete idMap[taskId];
  await writeIdMap(idMap);
}

export async function requestNotificationPermission(): Promise<boolean> {
  const notifications = await getNotificationsModule();
  if (!notifications) return false;
  try {
    const settings = await notifications.getPermissionsAsync();
    if (!settings.granted) {
      const requested = await notifications.requestPermissionsAsync();
      return requested.granted;
    }
    return settings.granted;
  } catch (error) {
    if (__DEV__) console.warn('Failed to request notification permission:', error);
    return false;
  }
}

export async function rescheduleAllReminders(tasks: any[]): Promise<void> {
  const notifications = await getNotificationsModule();
  if (!notifications) return;
  try {
    await notifications.cancelAllScheduledNotificationsAsync();
  } catch {
    // ignore
  }
  const idMap: Record<string, string[]> = {};

  for (const task of tasks) {
    const dueDate = task.dueDate ? new Date(task.dueDate) : null;

    const reminders: Reminder[] = task.reminders
      ? parseReminders(task.reminders)
      : task.reminder && task.reminder !== 'none'
        ? [task.reminder as Reminder]
        : [];

    if (reminders.length === 0 || !dueDate || !task.dueTime) continue;

    const granted = await requestNotificationPermission();
    if (!granted) continue;

    const [hours, mins] = (task.dueTime as string).split(':').map(Number);
    if (!Number.isNaN(hours)) dueDate.setHours(hours, Number.isNaN(mins) ? 0 : mins, 0, 0);

    const newIds: string[] = [];

    for (const reminder of reminders) {
      const minutes = reminderToMinutes(reminder);
      if (minutes === null) continue;

      const triggerDate = new Date(dueDate.getTime() - minutes * 60000);
      if (triggerDate.getTime() <= Date.now()) continue;

      try {
        const id = await notifications.scheduleNotificationAsync({
          content: {
            title: task.title,
            body: `Due ${task.dueTime}${task.repeat && task.repeat !== 'none' ? ' · repeating' : ''}`,
            sound: 'default',
          },
          trigger: { type: 'date', date: triggerDate },
        });
        newIds.push(id);
      } catch {
        // skip individual failures
      }
    }

    if (newIds.length > 0) idMap[task.id] = newIds;
  }

  await writeIdMap(idMap);
}
