import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { DialogScheduleTaskComponent } from './dialog-schedule-task.component';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatNativeDateModule } from '@angular/material/core';
import { provideMockStore } from '@ngrx/store/testing';
import { TranslateModule, TranslateService, TranslateStore } from '@ngx-translate/core';
import { SnackService } from '../../../core/snack/snack.service';
import { LocaleDatePipe } from 'src/app/ui/pipes/locale-date.pipe';
import { TaskService } from '../../../features/tasks/task.service';
import { WorkContextService } from '../../../features/work-context/work-context.service';
import { of } from 'rxjs';
import { PlannerService } from '../planner.service';
import { RootState } from '../../../root-store/root-state';
import {
  CONFIG_FEATURE_NAME,
  selectTimelineConfig,
} from '../../config/store/global-config.reducer';
import { TaskReminderOptionId } from '../../tasks/task.model';
import { ReminderService } from '../../reminder/reminder.service';
import { dateStrToUtcDate } from '../../../util/date-str-to-utc-date';
import { selectAllTasksWithDueTimeSorted } from '../../tasks/store/task.selectors';

describe('DialogScheduleTaskComponent - Select Due Only Mode', () => {
  let component: DialogScheduleTaskComponent;
  let fixture: ComponentFixture<DialogScheduleTaskComponent>;
  let dialogRefSpy: jasmine.SpyObj<MatDialogRef<DialogScheduleTaskComponent>>;
  let snackServiceSpy: jasmine.SpyObj<SnackService>;
  let taskServiceSpy: jasmine.SpyObj<TaskService>;
  let plannerServiceSpy: jasmine.SpyObj<PlannerService>;
  let workContextServiceSpy: jasmine.SpyObj<WorkContextService>;
  let reminderServiceSpy: jasmine.SpyObj<ReminderService>;

  beforeEach(async () => {
    dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['close']);
    snackServiceSpy = jasmine.createSpyObj('SnackService', ['open']);
    plannerServiceSpy = jasmine.createSpyObj('PlannerService', [
      'open',
      'getSnackExtraStr',
    ]);
    reminderServiceSpy = jasmine.createSpyObj('ReminderService', ['getById']);
    taskServiceSpy = jasmine.createSpyObj('TaskService', ['scheduleTask']);
    workContextServiceSpy = jasmine.createSpyObj(
      'WorkContextService',
      ['activeWorkContextId$'],
      {
        activeWorkContextId$: of('someWorkContextId'),
      },
    );

    await TestBed.configureTestingModule({
      imports: [
        DialogScheduleTaskComponent,
        MatDialogModule,
        ReactiveFormsModule,
        FormsModule,
        NoopAnimationsModule,
        MatFormFieldModule,
        MatInputModule,
        MatDatepickerModule,
        MatNativeDateModule,
        TranslateModule.forRoot(),
      ],
      providers: [
        provideMockStore<Partial<RootState>>({
          initialState: {
            [CONFIG_FEATURE_NAME]: {
              sync: {},
              localization: {
                lng: undefined,
                dateTimeLocale: undefined,
                firstDayOfWeek: 0,
              },
            } as any,
          },
          selectors: [
            { selector: selectAllTasksWithDueTimeSorted, value: [] },
            { selector: selectTimelineConfig, value: null },
          ],
        }),
        { provide: MatDialogRef, useValue: dialogRefSpy },
        { provide: SnackService, useValue: snackServiceSpy },
        { provide: ReminderService, useValue: reminderServiceSpy },
        { provide: PlannerService, useValue: plannerServiceSpy },
        { provide: TaskService, useValue: taskServiceSpy },
        { provide: WorkContextService, useValue: workContextServiceSpy },
        TranslateService,
        TranslateStore,
        LocaleDatePipe,
      ],
    }).compileComponents();
  });

  describe('isSelectDueOnly mode', () => {
    beforeEach(() => {
      TestBed.overrideProvider(MAT_DIALOG_DATA, {
        useValue: {
          isSelectDueOnly: true,
        },
      });

      fixture = TestBed.createComponent(DialogScheduleTaskComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
    });

    it('should initialize with isSelectDueOnly mode', () => {
      expect(component.data.isSelectDueOnly).toBe(true);
    });

    it('should set default reminder to AtStart in select-due-only mode', () => {
      expect(component.selectedReminderCfgId).toBe(TaskReminderOptionId.AtStart);
    });

    it('should return selected date and time instead of scheduling when submit is called', async () => {
      const testDate = new Date('2024-01-15T00:00:00.000Z');
      const testTime = '14:30';

      component.selectedDate = testDate;
      component.selectedTime = testTime;

      await component.submit();

      expect(dialogRefSpy.close).toHaveBeenCalledWith({
        date: testDate,
        time: testTime,
        remindOption: TaskReminderOptionId.AtStart,
      });
      expect(taskServiceSpy.scheduleTask).not.toHaveBeenCalled();
    });

    it('should return null time and null remindOption when no time selected', async () => {
      const testDate = new Date('2024-01-15T00:00:00.000Z');

      component.selectedDate = testDate;
      component.selectedTime = null;

      await component.submit();

      expect(dialogRefSpy.close).toHaveBeenCalledWith({
        date: testDate,
        time: null,
        remindOption: null,
      });
    });

    it('should not submit if no date is selected', async () => {
      component.selectedDate = null;
      component.selectedTime = '14:30';

      await component.submit();

      expect(dialogRefSpy.close).not.toHaveBeenCalled();
    });

    it('should handle remove operation gracefully in select-due-only mode', () => {
      component.remove();

      expect(dialogRefSpy.close).toHaveBeenCalledWith(false);
    });
  });

  describe('isSelectDueOnly mode with targetDay', () => {
    beforeEach(() => {
      TestBed.overrideProvider(MAT_DIALOG_DATA, {
        useValue: {
          isSelectDueOnly: true,
          targetDay: '2024-01-15',
        },
      });

      fixture = TestBed.createComponent(DialogScheduleTaskComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
    });

    it('should initialize with targetDay when provided', () => {
      expect(component.data.targetDay).toBe('2024-01-15');
    });

    it('should set selectedDate to targetDay on initialization', async () => {
      fixture.detectChanges();
      await component.ngAfterViewInit();

      const expectedDate = dateStrToUtcDate('2024-01-15');
      expect(component.selectedDate).toEqual(expectedDate);
    });
  });

  describe('isSelectDueOnly mode without targetDay', () => {
    beforeEach(() => {
      TestBed.overrideProvider(MAT_DIALOG_DATA, {
        useValue: {
          isSelectDueOnly: true,
        },
      });

      fixture = TestBed.createComponent(DialogScheduleTaskComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
    });

    it('should not have initial selectedDate when no targetDay provided', async () => {
      fixture.detectChanges();
      await component.ngAfterViewInit();

      // selectedDate should be null since no targetDay was provided
      expect(component.selectedDate).toBe(null);
    });
  });

  describe('Date selection in select-due-only mode', () => {
    beforeEach(() => {
      TestBed.overrideProvider(MAT_DIALOG_DATA, {
        useValue: {
          isSelectDueOnly: true,
        },
      });

      fixture = TestBed.createComponent(DialogScheduleTaskComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
    });

    it('should update selectedDate when dateSelected is called', fakeAsync(() => {
      const testDate = new Date('2024-01-20');

      component.dateSelected(testDate);
      // dateSelected defers the assignment via setTimeout — flush it before asserting
      tick();

      expect(component.selectedDate).toEqual(testDate);
    }));

    it('should handle quick access buttons correctly (triggering submit and closing dialog)', async () => {
      const initialDate = new Date();
      initialDate.setMinutes(0, 0, 0);

      // Test "Today" button
      await component.onQuickAccessClick('today');
      expect(component.selectedDate).toEqual(initialDate);
      expect(dialogRefSpy.close).toHaveBeenCalled();

      dialogRefSpy.close.calls.reset();

      // Test "Tomorrow" button
      const tomorrow = new Date(initialDate);
      tomorrow.setDate(tomorrow.getDate() + 1);
      await component.onQuickAccessClick('tomorrow');
      expect(component.selectedDate).toEqual(tomorrow);
      expect(dialogRefSpy.close).toHaveBeenCalled();
    });

    it('should NOT trigger submit when isSubmitOnQuickAccess is false', async () => {
      component.data.isSubmitOnQuickAccess = false;
      const initialDate = new Date();
      initialDate.setMinutes(0, 0, 0);

      await component.onQuickAccessClick('today');
      expect(component.selectedDate).toEqual(initialDate);
      expect(dialogRefSpy.close).not.toHaveBeenCalled();
    });
  });

  describe('Time handling in select-due-only mode', () => {
    beforeEach(() => {
      TestBed.overrideProvider(MAT_DIALOG_DATA, {
        useValue: {
          isSelectDueOnly: true,
        },
      });

      fixture = TestBed.createComponent(DialogScheduleTaskComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
    });

    it('should clear time and reminder when onTimeClear is called', () => {
      component.selectedTime = '10:30';
      component.selectedReminderCfgId = TaskReminderOptionId.m15;

      // Access the DateTimePickerComponent instance from the template
      const dateTimePicker = fixture.nativeElement.querySelector('datetime-picker');
      expect(dateTimePicker).toBeTruthy();

      // Trigger the event from DateTimePickerComponent
      component.selectedTime = null;
      component.selectedReminderCfgId = TaskReminderOptionId.DoNotRemind;

      expect(component.selectedTime).toBeNull();
      expect(component.selectedReminderCfgId).toBe(TaskReminderOptionId.DoNotRemind);
    });

    it('should autofill time on focus when no time is set', fakeAsync(() => {
      component.selectedDate = new Date(2026, 4, 6);
      component.selectedTime = null;

      // Simulate onTimeFocus being called from the picker
      // We can directly call the handler that would be bound in the template
      const dateTimePicker = fixture.debugElement.query(
        (debugEl) => debugEl.name === 'datetime-picker',
      );
      dateTimePicker.triggerEventHandler('timeChanged', '09:00');

      expect(component.selectedTime as unknown as string).toBe('09:00');
    }));
  });
});
