import { Injectable, inject } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import { of } from 'rxjs';
import { exhaustMap, switchMap, map, catchError, withLatestFrom, filter } from 'rxjs/operators';
import { PurchaseBillService } from '../../../services/purchase-bill.service';
import { PurchaseBillsActions } from './bills.actions';
import { selectPurchaseBillsLoaded } from './bills.reducer';

@Injectable()
export class PurchaseBillsEffects {
  private actions$ = inject(Actions);
  private store = inject(Store);
  private service = inject(PurchaseBillService);

  loadBills$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.loadBills),
      withLatestFrom(this.store.select(selectPurchaseBillsLoaded)),
      filter(([action, loaded]) => !loaded || action.force === true),
      exhaustMap(() =>
        this.service.listAll().pipe(
          map((res) =>
            PurchaseBillsActions.loadBillsSuccess({ bills: res.data, meta: res.meta })
          ),
          catchError((error) =>
            of(
              PurchaseBillsActions.loadBillsFailure({
                error: error.error?.message || 'Failed to load purchase bills',
              })
            )
          )
        )
      )
    )
  );

  loadBill$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.loadBill),
      exhaustMap(({ id }) =>
        this.service.getById(id).pipe(
          map((res) => PurchaseBillsActions.loadBillSuccess({ bill: res.data })),
          catchError((error) =>
            of(
              PurchaseBillsActions.loadBillFailure({
                error: error.error?.message || 'Failed to load purchase bill',
              })
            )
          )
        )
      )
    )
  );

  createBill$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.createBill),
      exhaustMap(({ data }) =>
        this.service.create(data).pipe(
          map((res) => PurchaseBillsActions.createBillSuccess({ bill: res.data })),
          catchError((error) =>
            of(
              PurchaseBillsActions.createBillFailure({
                error: error.error?.message || 'Failed to create purchase bill',
              })
            )
          )
        )
      )
    )
  );

  updateBill$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.updateBill),
      exhaustMap(({ id, data }) =>
        this.service.update(id, data).pipe(
          map((res) => PurchaseBillsActions.updateBillSuccess({ bill: res.data })),
          catchError((error) =>
            of(
              PurchaseBillsActions.updateBillFailure({
                error: error.error?.message || 'Failed to update purchase bill',
              })
            )
          )
        )
      )
    )
  );

  deleteBill$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.deleteBill),
      exhaustMap(({ id }) =>
        this.service.delete(id).pipe(
          map(() => PurchaseBillsActions.deleteBillSuccess({ id })),
          catchError((error) =>
            of(
              PurchaseBillsActions.deleteBillFailure({
                error: error.error?.message || 'Failed to delete purchase bill',
              })
            )
          )
        )
      )
    )
  );

  submitApproval$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.submitBillApproval),
      exhaustMap(({ id }) =>
        this.service.submitApproval(id).pipe(
          map((res) => PurchaseBillsActions.statusChangeSuccess({ bill: res.data })),
          catchError((error) =>
            of(
              PurchaseBillsActions.statusChangeFailure({
                error: error.error?.message || 'Failed to submit for approval',
              })
            )
          )
        )
      )
    )
  );

  approveBill$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.approveBill),
      exhaustMap(({ id }) =>
        this.service.approve(id).pipe(
          map((res) => PurchaseBillsActions.statusChangeSuccess({ bill: res.data })),
          catchError((error) =>
            of(
              PurchaseBillsActions.statusChangeFailure({
                error: error.error?.message || 'Failed to approve bill',
              })
            )
          )
        )
      )
    )
  );

  rejectBill$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.rejectBill),
      exhaustMap(({ id, data }) =>
        this.service.reject(id, data).pipe(
          map((res) => PurchaseBillsActions.statusChangeSuccess({ bill: res.data })),
          catchError((error) =>
            of(
              PurchaseBillsActions.statusChangeFailure({
                error: error.error?.message || 'Failed to reject bill',
              })
            )
          )
        )
      )
    )
  );

  postBill$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.postBill),
      exhaustMap(({ id }) =>
        this.service.post(id).pipe(
          map((res) => PurchaseBillsActions.statusChangeSuccess({ bill: res.data })),
          catchError((error) =>
            of(
              PurchaseBillsActions.statusChangeFailure({
                error: error.error?.message || 'Failed to post bill',
              })
            )
          )
        )
      )
    )
  );

  cancelBill$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.cancelBill),
      exhaustMap(({ id }) =>
        this.service.cancel(id).pipe(
          map((res) => PurchaseBillsActions.statusChangeSuccess({ bill: res.data })),
          catchError((error) =>
            of(
              PurchaseBillsActions.statusChangeFailure({
                error: error.error?.message || 'Failed to cancel bill',
              })
            )
          )
        )
      )
    )
  );

  recordPayment$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.recordBillPayment),
      exhaustMap(({ id, payment }) =>
        this.service.recordPayment(id, payment).pipe(
          switchMap(() =>
            this.service.getById(id).pipe(
              map((res) => PurchaseBillsActions.statusChangeSuccess({ bill: res.data }))
            )
          ),
          catchError((error) =>
            of(
              PurchaseBillsActions.statusChangeFailure({
                error: error.error?.message || 'Failed to record payment',
              })
            )
          )
        )
      )
    )
  );

  duplicateBill$ = createEffect(() =>
    this.actions$.pipe(
      ofType(PurchaseBillsActions.duplicateBill),
      exhaustMap(({ id }) =>
        this.service.duplicate(id).pipe(
          map((res) => PurchaseBillsActions.duplicateBillSuccess({ bill: res.data })),
          catchError((error) =>
            of(
              PurchaseBillsActions.statusChangeFailure({
                error: error.error?.message || 'Failed to duplicate bill',
              })
            )
          )
        )
      )
    )
  );
}
