import {Injectable} from "@angular/core";
import {Actions, createEffect, ofType} from "@ngrx/effects";
import {catchError, exhaustMap, map, of, switchMap, tap, zip} from "rxjs";
import {List} from "projects/mastodon-api/src/public-api";
import {fromApplication, fromListsPage, fromListViewPage, fromMastodonApi, fromMatrixViewPage, fromSyncPage, fromTableViewPage} from "../actions";
import {ListService} from "../../../services/list.service";
import {AccountService} from "../../../services/account.service";
import {NlNotificationService} from "../../../services/nl-notification.service";

@Injectable()
export class ApplicationStateEffects {
    loadLists$ = createEffect(() =>
        this.actions$.pipe(
            ofType(fromApplication.loadLists, fromSyncPage.loadLists),
            exhaustMap(() => this.listService.loadLists().pipe(
                    map(lists => fromMastodonApi.listsLoadedSuccess({lists})),
                    catchError(() => of(fromMastodonApi.listsLoadedError())),
                )
            )
        )
    );

    updateList$ = createEffect(() =>
        this.actions$.pipe(
            ofType(fromListsPage.updateList),
            exhaustMap((action) => this.listService.updateList(action.listId, action.newTitle).pipe(
                    map((resp) => {
                        const list = resp as List;
                        return fromMastodonApi.updateListSuccess({listId: list.id, newTitle: list.title});
                    }),
                    catchError(() => of(fromMastodonApi.updateListError)),
                )
            )
        )
    );
    updateListSuccess$ = createEffect(() =>
        this.actions$.pipe(
            ofType(fromMastodonApi.updateListSuccess),
            map(() => this.notificationService.success('List updated successfully', 'success')),
        ), {dispatch: false}
    );
    updateListError$ = createEffect(() =>
        this.actions$.pipe(
            ofType(fromMastodonApi.updateListError),
            map(() => this.notificationService.danger('Could not update the list', 'error')),
        ), {dispatch: false}
    );

    createList$ = createEffect(() =>
        this.actions$.pipe(
            ofType(fromListsPage.createList),
            exhaustMap((action) => this.listService.createList(action.title).pipe(
                    map((resp) => {
                        const list = resp as List;
                        return fromMastodonApi.createListSuccess({listId: list.id, title: list.title});
                    }),
                    catchError(() => of(fromMastodonApi.createListError())),
                )
            )
        )
    );
    createListSuccess$ = createEffect(() =>
        this.actions$.pipe(
            ofType(fromMastodonApi.createListSuccess),
            map(() => this.notificationService.success('List created successfully', 'success')),
        ), {dispatch: false}
    );
    createListError$ = createEffect(() =>
        this.actions$.pipe(
            ofType(fromMastodonApi.createListError),
            map(() => this.notificationService.danger('Could not create the list', 'error')),
        ), {dispatch: false}
    );

    listsLoaded$ = createEffect(() =>
        this.actions$.pipe(
            ofType(fromMastodonApi.listsLoadedSuccess),
            switchMap((action) => {
                    this.notificationService.success('Lists loaded successfully', 'success');
                    if (action.lists.length === 0) return of(fromMastodonApi.accountIdsForListsLoadedSuccess({mappings: {}}));
                    const mappingsArr = action.lists.map((list) => this.listService.loadAccountsIdsForList(list.id));
                    return zip(mappingsArr)
                        .pipe(
                            map(mappings => {
                                    const accountsInList: { [id: string]: string[] } = mappings.reduce((acc, mapping) => {
                                        return {...acc, ...mapping};
                                    }, {});
                                    return fromMastodonApi.accountIdsForListsLoadedSuccess({mappings: accountsInList})
                                }
                            ),
                        );
                }
            )
        )
    );

    loadFollowings$ = createEffect(() =>
        this.actions$.pipe(
            ofType(fromApplication.loadFollowings, fromSyncPage.loadFollowings),
            switchMap(() => this.accountService.loadFollowings().pipe(
                    map(followings => {
                        return fromMastodonApi.followingsLoadedSuccess({followings});
                    }),
                    catchError((error) => of(fromMastodonApi.followingsLoadedError({error}))),
                )
            )
        )
    );

    loadFollowingsSuccess$ = createEffect(() =>
            this.actions$.pipe(
                ofType(fromMastodonApi.followingsLoadedSuccess),
                tap(() => this.notificationService.success('Followings loaded successfully', 'success')),
            )
        , {dispatch: false});

    loadFollowingsError$ = createEffect(() =>
            this.actions$.pipe(
                ofType(fromMastodonApi.followingsLoadedError),
                tap(() => this.notificationService.danger('Followings could not be loaded', 'error')),
            )
        , {dispatch: false});

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

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

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

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

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

    constructor(
        private actions$: Actions,
        private listService: ListService,
        private accountService: AccountService,
        private notificationService: NlNotificationService,
    ) {
    }
}
