import { Injectable, inject } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import { of } from 'rxjs';
import { map, exhaustMap, catchError, withLatestFrom, filter } from 'rxjs/operators';
import { JournalEntryService } from '../../services/journal-entry.service';
import { JournalEntriesActions } from './journal-entries.actions';
import { selectJournalEntriesLoaded } from './journal-entries.reducer';

@Injectable()
export class JournalEntriesEffects {
  private actions$ = inject(Actions);
  private store = inject(Store);
  private journalEntryService = inject(JournalEntryService);

  loadJournalEntries$ = createEffect(() =>
    this.actions$.pipe(
      ofType(JournalEntriesActions.loadJournalEntries),
      withLatestFrom(this.store.select(selectJournalEntriesLoaded)),
      filter(([action, loaded]) => !loaded || !!action.forceReload),
      exhaustMap(() =>
        this.journalEntryService.listAll().pipe(
          map((response) =>
            JournalEntriesActions.loadJournalEntriesSuccess({ entries: response.data, meta: response.meta })
          ),
          catchError((error) =>
            of(JournalEntriesActions.loadJournalEntriesFailure({ error: error.error?.message || 'Failed to load journal entries' }))
          )
        )
      )
    )
  );

  loadJournalEntry$ = createEffect(() =>
    this.actions$.pipe(
      ofType(JournalEntriesActions.loadJournalEntry),
      exhaustMap(({ id }) =>
        this.journalEntryService.getById(id).pipe(
          map((response) => JournalEntriesActions.loadJournalEntrySuccess({ entry: response.data })),
          catchError((error) =>
            of(JournalEntriesActions.loadJournalEntryFailure({ error: error.error?.message || 'Failed to load journal entry' }))
          )
        )
      )
    )
  );

  createJournalEntry$ = createEffect(() =>
    this.actions$.pipe(
      ofType(JournalEntriesActions.createJournalEntry),
      exhaustMap(({ entry }) =>
        this.journalEntryService.create(entry).pipe(
          map((response) => JournalEntriesActions.createJournalEntrySuccess({ entry: response.data })),
          catchError((error) =>
            of(JournalEntriesActions.createJournalEntryFailure({ error: error.error?.message || 'Failed to create journal entry' }))
          )
        )
      )
    )
  );

  updateJournalEntry$ = createEffect(() =>
    this.actions$.pipe(
      ofType(JournalEntriesActions.updateJournalEntry),
      exhaustMap(({ id, entry }) =>
        this.journalEntryService.update(id, entry).pipe(
          map((response) => JournalEntriesActions.updateJournalEntrySuccess({ entry: response.data })),
          catchError((error) =>
            of(JournalEntriesActions.updateJournalEntryFailure({ error: error.error?.message || 'Failed to update journal entry' }))
          )
        )
      )
    )
  );

  deleteJournalEntry$ = createEffect(() =>
    this.actions$.pipe(
      ofType(JournalEntriesActions.deleteJournalEntry),
      exhaustMap(({ id }) =>
        this.journalEntryService.delete(id).pipe(
          map(() => JournalEntriesActions.deleteJournalEntrySuccess({ id })),
          catchError((error) =>
            of(JournalEntriesActions.deleteJournalEntryFailure({ error: error.error?.message || 'Failed to delete journal entry' }))
          )
        )
      )
    )
  );

  approveJournalEntry$ = createEffect(() =>
    this.actions$.pipe(
      ofType(JournalEntriesActions.approveJournalEntry),
      exhaustMap(({ id }) =>
        this.journalEntryService.approve(id).pipe(
          map((response) => JournalEntriesActions.approveJournalEntrySuccess({ entry: response.data })),
          catchError((error) =>
            of(JournalEntriesActions.approveJournalEntryFailure({ error: error.error?.message || 'Failed to approve journal entry' }))
          )
        )
      )
    )
  );

  postJournalEntry$ = createEffect(() =>
    this.actions$.pipe(
      ofType(JournalEntriesActions.postJournalEntry),
      exhaustMap(({ id }) =>
        this.journalEntryService.post(id).pipe(
          map((response) => JournalEntriesActions.postJournalEntrySuccess({ entry: response.data })),
          catchError((error) =>
            of(JournalEntriesActions.postJournalEntryFailure({ error: error.error?.message || 'Failed to post journal entry' }))
          )
        )
      )
    )
  );

  reverseJournalEntry$ = createEffect(() =>
    this.actions$.pipe(
      ofType(JournalEntriesActions.reverseJournalEntry),
      exhaustMap(({ id, date }) =>
        this.journalEntryService.reverse(id, date).pipe(
          map((response) => JournalEntriesActions.reverseJournalEntrySuccess({ entry: response.data })),
          catchError((error) =>
            of(JournalEntriesActions.reverseJournalEntryFailure({ error: error.error?.message || 'Failed to reverse journal entry' }))
          )
        )
      )
    )
  );

  cancelJournalEntry$ = createEffect(() =>
    this.actions$.pipe(
      ofType(JournalEntriesActions.cancelJournalEntry),
      exhaustMap(({ id }) =>
        this.journalEntryService.cancel(id).pipe(
          map((response) => JournalEntriesActions.cancelJournalEntrySuccess({ entry: response.data })),
          catchError((error) =>
            of(JournalEntriesActions.cancelJournalEntryFailure({ error: error.error?.message || 'Failed to cancel journal entry' }))
          )
        )
      )
    )
  );
}
