/**
 * 每日提醒/信用卡提醒（plan.md「4.4 每日提醒通知」）。
 *
 * 参考 BeeCount 的每日记账提醒 + 信用卡账单日/还款日提醒。
 * 通知调度通过注入抽象（生产用 expo-notifications，测试用 mock）。
 */

import type { CreditCard } from '../domain/finance/creditCards';

/** 通知调度器抽象（生产用 expo-notifications）。 */
export interface NotificationScheduler {
  /** 取消所有已调度的通知。 */
  cancelAll(): Promise<void>;
  /** 调度通知。 */
  schedule(params: {
    title: string;
    body: string;
    hour: number;
    minute: number;
    repeats: boolean;
    data?: Record<string, unknown>;
  }): Promise<string>;
  /** 调度按日期的通知（信用卡账单日/还款日）。 */
  scheduleByDay(params: {
    title: string;
    body: string;
    day: number;
    hour: number;
    minute: number;
    repeats: boolean;
  }): Promise<string>;
}

/**
 * 设置每日记账提醒（plan.md「4.4 setupDailyReminder」）。
 * @param hour 小时（0-23），默认 20
 * @param minute 分钟（0-59），默认 0
 */
export async function setupDailyReminder(
  scheduler: NotificationScheduler,
  hour = 20,
  minute = 0,
): Promise<string> {
  await scheduler.cancelAll();
  return scheduler.schedule({
    title: '记账提醒',
    body: '今天还没有记账哦，点击开始记录',
    hour, minute, repeats: true,
    data: { screen: 'add-transaction' },
  });
}

/**
 * 设置信用卡账单日/还款日提醒（plan.md「4.4 setupCreditCardReminder」）。
 * 账单日提前 1 天提醒，还款日提前 3 天提醒。
 */
export async function setupCreditCardReminder(
  scheduler: NotificationScheduler,
  card: CreditCard,
): Promise<string[]> {
  const ids: string[] = [];

  // 账单日提醒（提前1天，上午9点）
  const billingDay = ((card.billingDay - 1) + 28) % 28 + 1;  // 确保在 1-28 范围
  ids.push(await scheduler.scheduleByDay({
    title: `${card.name} 账单日提醒`,
    body: `明天是 ${card.name} 的账单日，请确认已记账`,
    day: billingDay, hour: 9, minute: 0, repeats: true,
  }));

  // 还款日提醒（提前3天，上午9点）
  const paymentDay = ((card.paymentDay - 3) + 28) % 28 + 1;
  ids.push(await scheduler.scheduleByDay({
    title: `${card.name} 还款提醒`,
    body: `还有3天是 ${card.name} 的还款日`,
    day: paymentDay, hour: 9, minute: 0, repeats: true,
  }));

  return ids;
}

/** 取消所有提醒。 */
export async function cancelAllReminders(scheduler: NotificationScheduler): Promise<void> {
  await scheduler.cancelAll();
}

/** 模拟通知调度器（测试用）。 */
export class MockNotificationScheduler implements NotificationScheduler {
  public scheduled: { title: string; body: string; hour: number; minute: number; repeats: boolean; day?: number }[] = [];
  public cancelled = false;
  private idCounter = 0;

  async cancelAll(): Promise<void> {
    this.cancelled = true;
    this.scheduled = [];
  }
  async schedule(params: { title: string; body: string; hour: number; minute: number; repeats: boolean }): Promise<string> {
    this.scheduled.push(params);
    return `notif-${++this.idCounter}`;
  }
  async scheduleByDay(params: { title: string; body: string; day: number; hour: number; minute: number; repeats: boolean }): Promise<string> {
    this.scheduled.push(params);
    return `notif-${++this.idCounter}`;
  }
}

/** expo-notifications 动态 require 的最小接口。 */
interface NotificationsModule {
  cancelAllScheduledNotificationsAsync(): Promise<void>;
  scheduleNotificationAsync(req: unknown): Promise<string>;
  SchedulableTriggerInputTypes: { DAILY: unknown; MONTHLY: unknown };
}

/**
 * 生产环境通知调度器：用 expo-notifications 调度真实本地通知。
 * 用动态 require 避免测试环境加载原生模块。
 */
export class RealNotificationScheduler implements NotificationScheduler {
  private getNotifications(): NotificationsModule | null {
    try {
      return require('expo-notifications');
    } catch {
      return null;
    }
  }

  async cancelAll(): Promise<void> {
    const n = this.getNotifications();
    if (n) await n.cancelAllScheduledNotificationsAsync();
  }

  async schedule(params: { title: string; body: string; hour: number; minute: number; repeats: boolean; data?: Record<string, unknown> }): Promise<string> {
    const n = this.getNotifications();
    if (!n) return 'unavailable';
    const id = await n.scheduleNotificationAsync({
      content: { title: params.title, body: params.body, data: params.data },
      trigger: {
        hour: params.hour,
        minute: params.minute,
        repeats: params.repeats,
        type: n.SchedulableTriggerInputTypes.DAILY,
      },
    });
    return id;
  }

  async scheduleByDay(params: { title: string; body: string; day: number; hour: number; minute: number; repeats: boolean }): Promise<string> {
    const n = this.getNotifications();
    if (!n) return 'unavailable';
    const id = await n.scheduleNotificationAsync({
      content: { title: params.title, body: params.body },
      trigger: {
        day: params.day,
        hour: params.hour,
        minute: params.minute,
        repeats: params.repeats,
        type: n.SchedulableTriggerInputTypes.MONTHLY,
      },
    });
    return id;
  }
}
