import { Maybe } from '@/core';
// import { ICustomerResponse } from '@/modules/customers/models';
import { computed, Inject, Injectable, InjectionToken, signal } from '@angular/core';
import { map, of } from 'rxjs';
import { IPosInfoResponse, IPosProductCategoriesResponse, IPosStockResponse } from '../models';
import { IPosInOrderProduct } from '../models/types';
import { PosService } from '../services/main.service';

export const POS_ID = new InjectionToken<number>('POS_ID');

export type TViewType = 'list' | 'grid';

interface IPosState {
  getStockLoading: boolean;
  stock: Maybe<IPosStockResponse[]>;
  getInfoLoading: boolean;
  info: Maybe<IPosInfoResponse>;
  getProductCategoriesLoading: boolean;
  productCategories: Maybe<IPosProductCategoriesResponse[]>;
  activeProductCategory: Maybe<number>;
  inOrderProducts: IPosInOrderProduct[];
  selectedCustomer: Maybe<any>;
  viewType: TViewType;
  searchQuery: string;
}

export const INITIAL_POS_STATE: IPosState = {
  getStockLoading: true,
  stock: null,
  getInfoLoading: true,
  info: null,
  getProductCategoriesLoading: true,
  productCategories: null,
  activeProductCategory: null,
  inOrderProducts: [],
  selectedCustomer: null,
  viewType: 'grid',
  searchQuery: '',
};

@Injectable({ providedIn: 'any' })
export class POSStore {
  constructor(
    @Inject(POS_ID) posId: number,
    private service: PosService,
  ) {
    this.posId = posId;
    this.initial();
  }

  private state$ = signal<IPosState>({ ...INITIAL_POS_STATE });

  readonly stock = computed(() => this.state$().stock);
  readonly getStockLoading = computed(() => this.state$().getStockLoading);
  readonly info = computed(() => this.state$().info);
  readonly getInfoLoading = computed(() => this.state$().getInfoLoading);
  readonly inOrderProducts = computed(() => this.state$().inOrderProducts);
  readonly productCategories = computed(() => this.state$().productCategories);
  readonly getProductCategoriesLoading = computed(() => this.state$().getProductCategoriesLoading);
  readonly activeProductCategory = computed(() => this.state$().activeProductCategory);
  readonly selectedCustomer = computed(() => this.state$().selectedCustomer);
  readonly viewType = computed(() => this.state$().viewType);
  readonly searchQuery = computed(() => this.state$().searchQuery);
  readonly activatedCategoryProducts = computed(() => {
    if (this.activeProductCategory() === 0) {
      return this.state$().stock;
    }
    return this.state$().stock?.filter(
      (s) => s.product.categoryId === this.state$().activeProductCategory,
    );
  });

  readonly orderPricingInfo = computed(() => {
    const totalAmount = this.inOrderProducts().reduce(
      (acc, curr) => acc + curr.unitPrice * curr.count,
      0,
    );
    const taxAmount = totalAmount * 0.1;
    return {
      totalItems: this.inOrderProducts().reduce((acc, curr) => acc + curr.count, 0),
      totalBaseAmount: this.inOrderProducts().reduce(
        (acc, curr) => acc + parseFloat(curr.product.salePrice) * curr.count,
        0,
      ),
      totalAmount,
      taxAmount,
      payableAmount: totalAmount + taxAmount,
    };
  });

  posId!: number;

  setState(partial: Partial<IPosState>) {
    this.state$.set({ ...this.state$(), ...partial });
  }

  reset() {
    this.state$.set({ ...INITIAL_POS_STATE });
  }

  initial() {
    this.getInfo();
    this.getStock();
    this.getProductCategories();
  }

  fillInitial(data: Partial<IPosState>) {
    this.state$.set({ ...INITIAL_POS_STATE, ...data });
  }

  getInfo() {
    this.setState({ getInfoLoading: true });
    this.service.getInfo(this.posId).subscribe({
      next: (res) => {
        this.setState({ info: res, getInfoLoading: false });
      },
      error: () => {
        this.setState({ getInfoLoading: false });
      },
    });
  }

  getStock() {
    this.setState({ getStockLoading: true });
    this.service.getStock(this.posId, this.state$().searchQuery).subscribe({
      next: (res) => {
        this.setState({ stock: res.data, getStockLoading: false });
      },
      error: () => {
        this.setState({ getStockLoading: false });
      },
    });
  }

  getProductCategories() {
    this.setState({ getProductCategoriesLoading: true });
    this.service
      .getCategories(this.posId)
      .pipe(
        map((res) => {
          return {
            data: [
              {
                id: 0,
                name: 'همه',
                totalQuantity: res.data.reduce((acc, curr) => acc + curr.totalQuantity, 0),
                productCount: res.data.reduce((acc, curr) => acc + curr.productCount, 0),
              },
              ...res.data,
            ],
            meta: res.meta,
          };
        }),
      )
      .subscribe({
        next: (res) => {
          this.setState({
            productCategories: res.data,
            getProductCategoriesLoading: false,
            activeProductCategory: res.data[0]?.id,
          });
        },
        error: () => {
          this.setState({ getProductCategoriesLoading: false });
        },
      });
  }

  changeActiveCategory(categoryId: number) {
    this.setState({ activeProductCategory: categoryId });
  }

  addToInOrderProducts(productId: number) {
    const productStock = this.state$().stock?.find((s) => s.product.id === productId);
    if (!productStock) return;
    const { product } = productStock;

    if (this.state$().inOrderProducts.some((p) => p.productId === productId)) {
      this.updateInOrderProductQuantity(
        productId,
        this.state$().inOrderProducts.find((p) => p.productId === productId)!.count + 1,
      );
    } else {
      this.setState({
        inOrderProducts: [
          ...this.state$().inOrderProducts,
          {
            product,
            productId: product.id,
            count: 1,
            unitPrice: parseFloat(product.salePrice),
            maxQuantity: productStock.quantity,
          },
        ],
      });
    }
  }

  removeFromInOrderProducts(productId: number) {
    this.setState({
      inOrderProducts: this.state$().inOrderProducts.filter((p) => p.productId !== productId),
    });
  }

  updateInOrderProductQuantity(productId: number, quantity: number) {
    const inOrderProducts = this.state$().inOrderProducts;

    if (quantity < 1) {
      this.removeFromInOrderProducts(productId);
    } else if (inOrderProducts.findIndex((p) => p.productId === productId) === -1) {
      this.addToInOrderProducts(productId);
    } else {
      const updatedProducts = inOrderProducts.map((p) => {
        if (p.productId === productId) {
          return { ...p, count: Math.min(quantity, p.maxQuantity) };
        }
        return p;
      });
      this.setState({ inOrderProducts: updatedProducts });
    }
  }

  updateInOrderProductFee(productId: number, unitPrice: number) {
    const inOrderProducts = this.state$().inOrderProducts;

    if (inOrderProducts.some((p) => p.productId === productId)) {
      const updatedProducts = inOrderProducts.map((p) => {
        if (p.productId === productId) {
          return { ...p, unitPrice: unitPrice };
        }
        return p;
      });
      this.setState({ inOrderProducts: updatedProducts });
    }
  }

  updateViewType(viewType: TViewType) {
    this.setState({ viewType });
  }
  updateSearchQuery(searchQuery: string) {
    this.setState({ searchQuery });
  }

  resetInOrderProducts() {
    this.setState({ inOrderProducts: [] });
  }

  setSelectedCustomer(customer: Maybe<any>) {
    this.setState({ selectedCustomer: customer });
  }

  submitOrder() {
    const orderPayload = {
      customer_id: this.selectedCustomer()?.id,
      items: this.inOrderProducts()?.map((item) => ({
        productId: item.productId,
        count: item.count,
        unitPrice: item.unitPrice,
      })),
    };
    return this.service.submitOrder(this.posId, orderPayload).subscribe({
      next: (res) => {
        this.resetInOrderProducts();
        this.setSelectedCustomer(null);
        return of(res);
      },
      error: (err) => {
        return of(err);
      },
    });
  }
}
