import { inject, Injectable } from '@angular/core';
import { createEffect, ofType } from '@ngrx/effects';
import { LOCAL_ACTIONS } from '../../../util/local-actions.token';
import { TaskSharedActions } from '../../../root-store/meta/task-shared.actions';
import { concatMap, filter, tap } from 'rxjs/operators';
import { truncate } from '../../../util/truncate';
import { devError } from '../../../util/dev-error';
import { T } from '../../../t.const';
import { SnackService } from '../../../core/snack/snack.service';
import { TaskService } from '../task.service';
import { Store } from '@ngrx/store';
import { LocaleDatePipe } from 'src/app/ui/pipes/locale-date.pipe';
import {
  IS_ANDROID_WEB_VIEW,
  IS_ANDROID_WEB_VIEW_TOKEN,
} from '../../../util/is-android-web-view';
import { androidInterface } from '../../android/android-interface';
import { generateNotificationId } from '../../android/android-notification-id.util';
import { PlannerActions } from '../../planner/store/planner.actions';
import { TaskLog } from '../../../core/log';

@Injectable()
export class TaskReminderEffects {
  private _localActions$ = inject(LOCAL_ACTIONS);
  private _snackService = inject(SnackService);
  private _taskService = inject(TaskService);
  private _store = inject(Store);
  private _datePipe = inject(LocaleDatePipe);
  private _isAndroidWebView = inject(IS_ANDROID_WEB_VIEW_TOKEN);

  snack$ = createEffect(
    () =>
      this._localActions$.pipe(
        ofType(TaskSharedActions.scheduleTaskWithTime),
        tap(({ task, remindAt, dueWithTime }) => {
          if (!Number.isFinite(dueWithTime)) {
            devError(
              'scheduleTaskWithTime dispatched with invalid dueWithTime: ' + dueWithTime,
            );
          }
          const formattedDate = this._datePipe.transform(dueWithTime, 'short');
          this._snackService.open({
            type: 'SUCCESS',
            translateParams: {
              title: truncate(task.title),
              date: formattedDate || '',
            },
            msg: T.F.TASK.S.REMINDER_ADDED,
            ico: remindAt ? 'alarm' : 'schedule',
          });
        }),
      ),
    { dispatch: false },
  );

  // NOTE: autoMoveToBacklog is now handled atomically in the meta-reducer
  // (task-shared-scheduling.reducer.ts) to ensure atomic consistency.
  // The isMoveToBacklog flag in scheduleTaskWithTime action is processed
  // directly in handleScheduleTaskWithTime().

  updateTaskReminderSnack$ = createEffect(
    () =>
      this._localActions$.pipe(
        ofType(TaskSharedActions.reScheduleTaskWithTime),
        filter(({ remindAt }) => typeof remindAt === 'number'),
        tap(({ task }) =>
          this._snackService.open({
            type: 'SUCCESS',
            translateParams: {
              title: truncate(task.title),
            },
            msg: T.F.TASK.S.REMINDER_UPDATED,
            ico: 'schedule',
          }),
        ),
      ),
    { dispatch: false },
  );

  // NOTE: autoMoveToBacklogOnReschedule is now handled atomically in the meta-reducer
  // (task-shared-scheduling.reducer.ts) to ensure atomic consistency.
  // The isMoveToBacklog flag in reScheduleTaskWithTime action is processed
  // directly in handleScheduleTaskWithTime().

  unscheduleDoneTask$ = createEffect(
    () =>
      this._localActions$.pipe(
        ofType(TaskSharedActions.updateTask),
        filter(({ task }) => !!task.changes.isDone),
        concatMap(({ task }) => this._taskService.getByIdOnce$(task.id as string)),
        tap((task) => {
          if (task?.remindAt) {
            // On Android, immediately cancel native reminder to prevent notifications
            // for done tasks. This is necessary because the reactive cancellation
            // via reminders$ observable can have a delay.
            if (IS_ANDROID_WEB_VIEW) {
              try {
                const notificationId = generateNotificationId(task.id);
                androidInterface.cancelNativeReminder?.(notificationId);
              } catch (e) {
                TaskLog.err('Failed to cancel native reminder:', e);
              }
            }

            this._store.dispatch(
              TaskSharedActions.dismissReminderOnly({
                id: task.id,
              }),
            );
          }

          // Clear deadline reminder when task is done (keep the deadline date for reference)
          if (task?.deadlineRemindAt) {
            if (IS_ANDROID_WEB_VIEW) {
              try {
                const notificationId = generateNotificationId(task.id + '_deadline');
                androidInterface.cancelNativeReminder?.(notificationId);
              } catch (e) {
                TaskLog.err('Failed to cancel native deadline reminder:', e);
              }
            }

            this._store.dispatch(
              TaskSharedActions.clearDeadlineReminder({ taskId: task.id }),
            );
          }
        }),
      ),
    { dispatch: false },
  );

  unscheduleSnack$ = createEffect(
    () =>
      this._localActions$.pipe(
        ofType(TaskSharedActions.unscheduleTask),
        filter(({ isSkipToast }) => !isSkipToast),
        tap(() => {
          this._snackService.open({
            type: 'SUCCESS',
            msg: T.F.TASK.S.REMINDER_DELETED,
            ico: 'schedule',
          });
        }),
      ),
    { dispatch: false },
  );

  setDeadlineSnack$ = createEffect(
    () =>
      this._localActions$.pipe(
        ofType(TaskSharedActions.setDeadline),
        tap(({ deadlineDay, deadlineWithTime }) => {
          const formattedDate = deadlineWithTime
            ? this._datePipe.transform(deadlineWithTime, 'short')
            : deadlineDay
              ? this._datePipe.transform(deadlineDay, 'shortDate')
              : '';
          this._snackService.open({
            type: 'SUCCESS',
            translateParams: { date: formattedDate || '' },
            msg: T.F.TASK.S.DEADLINE_SET,
            ico: 'flag',
          });
        }),
      ),
    { dispatch: false },
  );

  removeDeadlineSnack$ = createEffect(
    () =>
      this._localActions$.pipe(
        ofType(TaskSharedActions.removeDeadline),
        tap(() => {
          this._snackService.open({
            type: 'SUCCESS',
            msg: T.F.TASK.S.DEADLINE_REMOVED,
            ico: 'flag',
          });
        }),
      ),
    { dispatch: false },
  );

  dismissReminderSnack$ = createEffect(
    () =>
      this._localActions$.pipe(
        ofType(TaskSharedActions.dismissReminderOnly),
        tap(() => {
          this._snackService.open({
            type: 'SUCCESS',
            msg: T.F.TASK.S.REMINDER_DELETED,
            ico: 'schedule',
          });
        }),
      ),
    { dispatch: false },
  );

  // Cancel native Android reminders when reminder is removed or dismissed
  // Uses injection token with filter for testability (unlike other Android effects)
  cancelNativeReminderOnUnschedule$ = createEffect(
    () =>
      this._localActions$.pipe(
        ofType(
          TaskSharedActions.unscheduleTask,
          TaskSharedActions.dismissReminderOnly,
          TaskSharedActions.removeDeadline,
          TaskSharedActions.clearDeadlineReminder,
        ),
        filter(() => this._isAndroidWebView),
        tap((action) => {
          const taskId = 'id' in action ? action.id : action.taskId;
          try {
            const notificationId = generateNotificationId(taskId);
            androidInterface.cancelNativeReminder?.(notificationId);
            // Also cancel the deadline-specific notification if it exists
            if ('taskId' in action) {
              const deadlineNotificationId = generateNotificationId(taskId + '_deadline');
              androidInterface.cancelNativeReminder?.(deadlineNotificationId);
            }
          } catch (e) {
            TaskLog.err('Failed to cancel native reminder:', e);
          }
        }),
      ),
    { dispatch: false },
  );

  // Cancel native Android reminders when reminder dialog actions are taken
  // (snooze, add to today, plan for tomorrow)
  cancelNativeReminderOnDialogAction$ = createEffect(
    () =>
      this._localActions$.pipe(
        ofType(
          TaskSharedActions.reScheduleTaskWithTime,
          TaskSharedActions.planTasksForToday,
          PlannerActions.planTaskForDay,
        ),
        filter(() => this._isAndroidWebView),
        tap((action) => {
          const ids = 'taskIds' in action ? action.taskIds : [action.task.id];
          ids.forEach((id) => {
            try {
              const notificationId = generateNotificationId(id);
              androidInterface.cancelNativeReminder?.(notificationId);
            } catch (e) {
              TaskLog.err('Failed to cancel native reminder:', e);
            }
          });
        }),
      ),
    { dispatch: false },
  );

  // Cancel native Android reminders when tasks are deleted
  cancelNativeRemindersOnDelete$ =
    IS_ANDROID_WEB_VIEW &&
    createEffect(
      () =>
        this._localActions$.pipe(
          ofType(TaskSharedActions.deleteTask),
          tap(({ task }) => {
            const deletedTaskIds = [task.id, ...task.subTaskIds];
            deletedTaskIds.forEach((id) => {
              try {
                androidInterface.cancelNativeReminder?.(generateNotificationId(id));
                androidInterface.cancelNativeReminder?.(
                  generateNotificationId(id + '_deadline'),
                );
              } catch (e) {
                TaskLog.err('Failed to cancel native reminder:', e);
              }
            });
          }),
        ),
      { dispatch: false },
    );

  // Cancel native Android reminders when multiple tasks are deleted
  cancelNativeRemindersOnBulkDelete$ =
    IS_ANDROID_WEB_VIEW &&
    createEffect(
      () =>
        this._localActions$.pipe(
          ofType(TaskSharedActions.deleteTasks),
          tap(({ taskIds }) => {
            taskIds.forEach((id) => {
              try {
                androidInterface.cancelNativeReminder?.(generateNotificationId(id));
                androidInterface.cancelNativeReminder?.(
                  generateNotificationId(id + '_deadline'),
                );
              } catch (e) {
                TaskLog.err('Failed to cancel native reminder:', e);
              }
            });
          }),
        ),
      { dispatch: false },
    );

  // Cancel native Android reminders when tasks are archived
  cancelNativeRemindersOnArchive$ =
    IS_ANDROID_WEB_VIEW &&
    createEffect(
      () =>
        this._localActions$.pipe(
          ofType(TaskSharedActions.moveToArchive),
          tap(({ tasks }) => {
            tasks.forEach((task) => {
              try {
                androidInterface.cancelNativeReminder?.(generateNotificationId(task.id));
                androidInterface.cancelNativeReminder?.(
                  generateNotificationId(task.id + '_deadline'),
                );
              } catch (e) {
                TaskLog.err('Failed to cancel native reminder:', e);
              }
              // Also cancel for subtasks
              task.subTaskIds?.forEach((subId) => {
                try {
                  androidInterface.cancelNativeReminder?.(generateNotificationId(subId));
                  androidInterface.cancelNativeReminder?.(
                    generateNotificationId(subId + '_deadline'),
                  );
                } catch (e) {
                  TaskLog.err('Failed to cancel native reminder:', e);
                }
              });
            });
          }),
        ),
      { dispatch: false },
    );
}
