import { inject, Injectable } from '@angular/core';
import { Router } from '@angular/router';

import { Store, Action, Selector, State, StateContext } from '@ngxs/store';
import { tap } from 'rxjs';

import {
  GetProductsAction,
  GetStoreProductsAction,
  GetCategoryProductsAction,
  GetRelatedProductsAction,
  GetProductBySlugAction,
  GetDealProductsAction,
  GetProductBySearchAction,
  GetMenuProductsAction,
  GetProductByIdsAction,
} from '../action/product.action';
import { IProduct, IProductModel } from '../interface/product.interface';
import { ProductService } from '../services/product.service';
import { ThemeOptionService } from '../services/theme-option.service';

export class ProductStateModel {
  product = {
    data: [] as IProduct[],
    total: 0,
  };
  selectedProduct: IProduct | null;
  categoryProducts: IProduct[] | [];
  relatedProducts: IProduct[] | [];
  storeProducts: IProduct[] | [];
  dealProducts: IProduct[] | [];
  menuProducts: IProduct[] | [];
  productBySearch: IProduct[] | [];
  productByIds: IProduct[] | [];
}

@State<ProductStateModel>({
  name: 'product',
  defaults: {
    product: {
      data: [],
      total: 0,
    },
    selectedProduct: null,
    categoryProducts: [],
    relatedProducts: [],
    storeProducts: [],
    dealProducts: [],
    menuProducts: [],
    productBySearch: [],
    productByIds: [],
  },
})
@Injectable()
export class ProductState {
  private store = inject(Store);
  private router = inject(Router);
  private productService = inject(ProductService);
  private themeOptionService = inject(ThemeOptionService);

  @Selector()
  static product(state: ProductStateModel) {
    return state.product;
  }

  @Selector()
  static selectedProduct(state: ProductStateModel) {
    return state.selectedProduct;
  }

  @Selector()
  static relatedProducts(state: ProductStateModel) {
    return state.relatedProducts;
  }

  @Selector()
  static categoryProducts(state: ProductStateModel) {
    return state.categoryProducts;
  }

  @Selector()
  static storeProducts(state: ProductStateModel) {
    return state.storeProducts;
  }

  @Selector()
  static dealProducts(state: ProductStateModel) {
    return state.dealProducts;
  }

  @Selector()
  static menuProducts(state: ProductStateModel) {
    return state.menuProducts;
  }

  @Selector()
  static productBySearch(state: ProductStateModel) {
    return state.productBySearch;
  }

  @Selector()
  static productByIds(state: ProductStateModel) {
    return state.productByIds;
  }

  @Action(GetProductsAction)
  getProducts(ctx: StateContext<ProductStateModel>, action: GetProductsAction) {
    this.productService.skeletonLoader = true;
    return this.productService.getProducts(action.payload).pipe(
      tap({
        next: (result: IProductModel) => {
          ctx.patchState({
            product: {
              data: result.data,
              total: result?.total ? result?.total : result.data?.length,
            },
          });
        },
        complete: () => {
          this.productService.skeletonLoader = false;
        },
        error: err => {
          throw new Error(err?.error?.message);
        },
      }),
    );
  }

  @Action(GetRelatedProductsAction)
  getRelatedProducts(ctx: StateContext<ProductStateModel>, action: GetProductsAction) {
    this.themeOptionService.preloader = true;
    return this.productService.getProducts(action.payload).pipe(
      tap({
        next: (result: IProductModel) => {
          const state = ctx.getState();
          ctx.patchState({
            ...state,
            relatedProducts: result.data,
          });
        },
        complete: () => {
          this.themeOptionService.preloader = false;
        },
        error: err => {
          throw new Error(err?.error?.message);
        },
      }),
    );
  }

  @Action(GetCategoryProductsAction)
  getCategoryProducts(ctx: StateContext<ProductStateModel>, action: GetProductsAction) {
    return this.productService.getProducts(action.payload).pipe(
      tap({
        next: (result: IProductModel) => {
          const state = ctx.getState();
          ctx.patchState({
            ...state,
            product: {
              data: [...state.product.data, ...result.data],
              total: state.product.data.length + result.data.length,
            },
            categoryProducts: result.data,
          });
        },
        complete: () => {
          this.themeOptionService.preloader = false;
        },
        error: err => {
          throw new Error(err?.error?.message);
        },
      }),
    );
  }

  @Action(GetStoreProductsAction)
  getStoreProducts(ctx: StateContext<ProductStateModel>, action: GetProductsAction) {
    return this.productService.getProducts(action.payload).pipe(
      tap({
        next: (result: IProductModel) => {
          const state = ctx.getState();
          ctx.patchState({
            ...state,
            storeProducts: result.data,
          });
        },
        error: err => {
          throw new Error(err?.error?.message);
        },
      }),
    );
  }

  @Action(GetProductBySlugAction)
  getProductBySlug(ctx: StateContext<ProductStateModel>, { slug }: GetProductBySlugAction) {
    this.themeOptionService.preloader = true;
    return this.productService.getProductBySlug(slug).pipe(
      tap({
        next: result => {
          result.related_products =
            result.related_products && result.related_products.length
              ? result.related_products
              : [];
          result.cross_sell_products =
            result.cross_sell_products && result.cross_sell_products.length
              ? result.cross_sell_products
              : [];

          const ids = [...result.related_products, ...result.cross_sell_products];
          const categoryIds = [...result?.categories?.map(category => category.id)];
          this.store.dispatch(
            new GetRelatedProductsAction({
              ids: ids?.join(','),
              category_ids: categoryIds?.join(','),
              status: 1,
            }),
          );

          const state = ctx.getState();
          ctx.patchState({
            ...state,
            selectedProduct: result,
          });
        },
        complete: () => {
          this.themeOptionService.preloader = false;
        },
        error: err => {
          void this.router.navigate(['/404']);
          throw new Error(err?.error?.message);
        },
      }),
    );
  }

  @Action(GetDealProductsAction)
  getDealProducts(ctx: StateContext<ProductStateModel>, action: GetDealProductsAction) {
    return this.productService.getProducts(action.payload).pipe(
      tap({
        next: (result: IProductModel) => {
          const state = ctx.getState();
          ctx.patchState({
            ...state,
            dealProducts: result.data,
          });
        },
        error: err => {
          throw new Error(err?.error?.message);
        },
      }),
    );
  }

  @Action(GetMenuProductsAction)
  getMenuProducts(ctx: StateContext<ProductStateModel>, action: GetMenuProductsAction) {
    return this.productService.getProducts(action.payload).pipe(
      tap({
        next: (result: IProductModel) => {
          const state = ctx.getState();
          ctx.patchState({
            ...state,
            menuProducts: result.data,
          });
        },
        error: err => {
          throw new Error(err?.error?.message);
        },
      }),
    );
  }

  @Action(GetProductBySearchAction)
  getProductBySearch(ctx: StateContext<ProductStateModel>, action: GetProductBySearchAction) {
    this.productService.searchSkeleton = true;
    return this.productService.getProductBySearch(action.payload).pipe(
      tap({
        next: result => {
          ctx.patchState({
            productBySearch: result,
          });
        },
        complete: () => {
          this.productService.searchSkeleton = false;
        },
        error: err => {
          throw new Error(err?.error?.message);
        },
      }),
    );
  }

  @Action(GetProductByIdsAction)
  getProductByIds(ctx: StateContext<ProductStateModel>, action: GetProductByIdsAction) {
    return this.productService.getProducts(action.payload).pipe(
      tap({
        next: (result: IProductModel) => {
          if (result) {
            const state = ctx.getState();
            ctx.patchState({
              ...state,
              productByIds: result.data,
            });
          }
        },
        error: err => {
          throw new Error(err?.error?.message);
        },
      }),
    );
  }
}
