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, switchMap, catchError, withLatestFrom, filter } from 'rxjs/operators';
import { SalesInvoiceService } from '../../../services/sales-invoice.service';
import { SalesInvoicesActions } from './invoices.actions';
import { selectSalesInvoicesLoaded } from './invoices.reducer';

@Injectable()
export class SalesInvoicesEffects {
  private actions$ = inject(Actions);
  private store = inject(Store);
  private invoiceService = inject(SalesInvoiceService);

  loadInvoices$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.loadInvoices),
      withLatestFrom(this.store.select(selectSalesInvoicesLoaded)),
      filter(([action, loaded]) => !!action.force || !loaded),
      exhaustMap(() =>
        this.invoiceService.listAll().pipe(
          map((response) =>
            SalesInvoicesActions.loadInvoicesSuccess({
              invoices: response.data,
              meta: response.meta,
            })
          ),
          catchError((error) =>
            of(
              SalesInvoicesActions.loadInvoicesFailure({
                error: error.error?.message || 'Failed to load invoices',
              })
            )
          )
        )
      )
    )
  );

  loadInvoice$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.loadInvoice),
      exhaustMap(({ id }) =>
        this.invoiceService.getById(id).pipe(
          map((response) =>
            SalesInvoicesActions.loadInvoiceSuccess({ invoice: response.data })
          ),
          catchError((error) =>
            of(
              SalesInvoicesActions.loadInvoiceFailure({
                error: error.error?.message || 'Failed to load invoice',
              })
            )
          )
        )
      )
    )
  );

  createInvoice$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.createInvoice),
      exhaustMap(({ data }) =>
        this.invoiceService.create(data).pipe(
          map((response) =>
            SalesInvoicesActions.createInvoiceSuccess({ invoice: response.data })
          ),
          catchError((error) =>
            of(
              SalesInvoicesActions.createInvoiceFailure({
                error: error.error?.message || 'Failed to create invoice',
              })
            )
          )
        )
      )
    )
  );

  updateInvoice$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.updateInvoice),
      exhaustMap(({ id, data }) =>
        this.invoiceService.update(id, data).pipe(
          map((response) =>
            SalesInvoicesActions.updateInvoiceSuccess({ invoice: response.data })
          ),
          catchError((error) =>
            of(
              SalesInvoicesActions.updateInvoiceFailure({
                error: error.error?.message || 'Failed to update invoice',
              })
            )
          )
        )
      )
    )
  );

  deleteInvoice$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.deleteInvoice),
      exhaustMap(({ id }) =>
        this.invoiceService.delete(id).pipe(
          map(() => SalesInvoicesActions.deleteInvoiceSuccess({ id })),
          catchError((error) =>
            of(
              SalesInvoicesActions.deleteInvoiceFailure({
                error: error.error?.message || 'Failed to delete invoice',
              })
            )
          )
        )
      )
    )
  );

  submitInvoiceApproval$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.submitInvoiceApproval),
      exhaustMap(({ id }) =>
        this.invoiceService.submitApproval(id).pipe(
          map((response) =>
            SalesInvoicesActions.submitInvoiceApprovalSuccess({ invoice: response.data })
          ),
          catchError((error) =>
            of(
              SalesInvoicesActions.submitInvoiceApprovalFailure({
                error: error.error?.message || 'Failed to submit invoice for approval',
              })
            )
          )
        )
      )
    )
  );

  approveInvoice$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.approveInvoice),
      exhaustMap(({ id }) =>
        this.invoiceService.approve(id).pipe(
          map((response) =>
            SalesInvoicesActions.approveInvoiceSuccess({ invoice: response.data })
          ),
          catchError((error) =>
            of(
              SalesInvoicesActions.approveInvoiceFailure({
                error: error.error?.message || 'Failed to approve invoice',
              })
            )
          )
        )
      )
    )
  );

  rejectInvoice$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.rejectInvoice),
      exhaustMap(({ id }) =>
        this.invoiceService.reject(id).pipe(
          map((response) =>
            SalesInvoicesActions.rejectInvoiceSuccess({ invoice: response.data })
          ),
          catchError((error) =>
            of(
              SalesInvoicesActions.rejectInvoiceFailure({
                error: error.error?.message || 'Failed to reject invoice',
              })
            )
          )
        )
      )
    )
  );

  postInvoice$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.postInvoice),
      exhaustMap(({ id }) =>
        this.invoiceService.post(id).pipe(
          map((response) =>
            SalesInvoicesActions.postInvoiceSuccess({ invoice: response.data })
          ),
          catchError((error) =>
            of(
              SalesInvoicesActions.postInvoiceFailure({
                error: error.error?.message || 'Failed to post invoice',
              })
            )
          )
        )
      )
    )
  );

  cancelInvoice$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.cancelInvoice),
      exhaustMap(({ id }) =>
        this.invoiceService.cancel(id).pipe(
          map((response) =>
            SalesInvoicesActions.cancelInvoiceSuccess({ invoice: response.data })
          ),
          catchError((error) =>
            of(
              SalesInvoicesActions.cancelInvoiceFailure({
                error: error.error?.message || 'Failed to cancel invoice',
              })
            )
          )
        )
      )
    )
  );

  recordInvoicePayment$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.recordInvoicePayment),
      exhaustMap(({ id, payment }) =>
        this.invoiceService.recordPayment(id, payment).pipe(
          // recordPayment returns SalesPayment, not SalesInvoice.
          // After recording, reload the invoice to get updated totals/status.
          switchMap(() => this.invoiceService.getById(id)),
          map((response) =>
            SalesInvoicesActions.recordInvoicePaymentSuccess({ invoice: response.data })
          ),
          catchError((error) =>
            of(
              SalesInvoicesActions.recordInvoicePaymentFailure({
                error: error.error?.message || 'Failed to record payment',
              })
            )
          )
        )
      )
    )
  );

  duplicateInvoice$ = createEffect(() =>
    this.actions$.pipe(
      ofType(SalesInvoicesActions.duplicateInvoice),
      exhaustMap(({ id }) =>
        this.invoiceService.duplicate(id).pipe(
          map((response) =>
            SalesInvoicesActions.duplicateInvoiceSuccess({ invoice: response.data })
          ),
          catchError((error) =>
            of(
              SalesInvoicesActions.duplicateInvoiceFailure({
                error: error.error?.message || 'Failed to duplicate invoice',
              })
            )
          )
        )
      )
    )
  );
}
