import {Injectable} from "@angular/core";
import {act, Actions, createEffect, ofType} from "@ngrx/effects";
import {ListActions, MastodonApiActions} from "./actions";
import {catchError, concat, exhaustMap, forkJoin, map, merge, of, switchMap} from "rxjs";
import {ListService} from "../../services/list.service";
import {AccountService} from "../../services/account.service";
import {NbToastrService} from "@nebular/theme";

@Injectable()
export class ApplicationEffects {
    loadLists$ = createEffect(() =>
        this.actions$.pipe(
            ofType(MastodonApiActions.loadLists),
            exhaustMap(() => this.listService.loadLists().pipe(
                    map(lists => MastodonApiActions.listsLoaded({lists})),
                    catchError(() => of(MastodonApiActions.listsLoadedError())),
                )
            )
        )
    );

    listsLoaded$ = createEffect(() =>
        this.actions$.pipe(
            ofType(MastodonApiActions.listsLoaded),
            switchMap((action) => {
                    return forkJoin(action.lists.map(list => this.listService.loadAccountsIdsForList(list.id)))
                        .pipe(
                            map(mappings => {
                                    const accountsInList: { [id: string]: string[] } = mappings.reduce((acc, mapping) => {
                                        return {...acc, ...mapping};
                                    }, {});
                                    return MastodonApiActions.accountIdsForListsLoaded({mappings: accountsInList})
                                }
                            ),
                        );
                }
            )
        )
    );

    loadFollowings$ = createEffect(() =>
        this.actions$.pipe(
            ofType(MastodonApiActions.loadFollowings),
            exhaustMap(() => this.accountService.loadFollowings().pipe(
                    map(followings => MastodonApiActions.followingsLoaded({followings})),
                    catchError((error) => of(MastodonApiActions.followingsLoadedError({error}))),
                )
            )
        )
    );

    addAccountToList$ = createEffect(() =>
        this.actions$.pipe(
            ofType(ListActions.addAccountToList),
            exhaustMap((action) => this.listService.addAccountToSelectedList(action.accountId, action.listId).pipe(
                    map(() => ListActions.addAccountToListSuccess()),
                    catchError(() => of(ListActions.addAccountToListError())),
                )
            )
        )
    );

    addAccountToListSuccess$ = createEffect(() =>
        this.actions$.pipe(
            ofType(ListActions.addAccountToListSuccess),
            map(() => this.toastService.success('Account added to list', 'Success')),
        ), {dispatch: false}
    );

    addCountsToListError$ = createEffect(() =>
        this.actions$.pipe(
            ofType(ListActions.addAccountToListError),
            map(() => this.toastService.danger('Could not add account to list, please reload', 'Error')),
        ), {dispatch: false}
    );

    removeAccountFromList$ = createEffect(() =>
        this.actions$.pipe(
            ofType(ListActions.removeAccountFromList),
            exhaustMap((action) => this.listService.removeAccountFromList(action.accountId, action.listId).pipe(
                    map(() => ListActions.removeAccountFromListSuccess()),
                    catchError(() => of(ListActions.removeAccountFromListError())),
                )
            )
        )
    );
    removeAccountFromListSuccess$ = createEffect(() =>
        this.actions$.pipe(
            ofType(ListActions.removeAccountFromListSuccess),
            map(() => this.toastService.success('Account removed from list', 'Success')),
        ), {dispatch: false}
    );

    removeAccountFromListError$ = createEffect(() =>
        this.actions$.pipe(
            ofType(ListActions.removeAccountFromListError),
            map(() => this.toastService.danger('Could not remove account from list, please reload', 'Error')),
        ), {dispatch: false}
    );

    constructor(
        private actions$: Actions,
        private listService: ListService,
        private accountService: AccountService,
        private toastService: NbToastrService,
    ) {
    }
}
