import {
  createAsyncThunk,
  createSelector,
  createSlice,
  PayloadAction,
} from "@reduxjs/toolkit";
import { sum } from "lodash";
import { RootState } from "../../store";

import {
  BookingPriceDetail,
  BookingPriceDetails,
  BookingProductNotification,
} from "@qite/tide-client/build/types";
import { setPackage } from "../booking/booking-slice";
import {
  selectBookingPackageBookRequest,
  selectLanguageCode,
  selectPackageDetails,
} from "../booking/selectors";
import priceDetailsApi from "./price-details-api";
import { updatePackageDetails } from "./util";

export interface PriceDetailsState {
  priceDetails: BookingPriceDetail[];
  notifications: BookingProductNotification[];
  deposit?: number;
  isBusy: boolean;
}

export const fetchPriceDetails = createAsyncThunk(
  "priceDetails/fetchPriceDetails",
  async (_, { dispatch, getState, signal }) => {
    dispatch(setFetchingPriceDetails(true));
    dispatch(resetPriceDetails());

    const state = getState() as RootState;
    const languageCode = selectLanguageCode(state);

    const request = selectBookingPackageBookRequest(state);

    if (!request) {
      throw Error("Product not available.");
    }

    const priceDetails: BookingPriceDetails =
      await priceDetailsApi.fetchPriceDetails(
        state.apiSettings,
        request,
        signal,
        languageCode
      );

    dispatch(setFetchingPriceDetails(false));

    const packageDetails = selectPackageDetails(state);
    if (packageDetails) {
      const updatedPackageDetails = updatePackageDetails(
        packageDetails,
        priceDetails.changedLines
      );
      dispatch(setPackage(updatedPackageDetails));
    }

    return priceDetails;
  }
);

const priceDetailsSlice = createSlice({
  name: "priceDetails",
  initialState: <PriceDetailsState>{
    priceDetails: [],
    notifications: [],
    isBusy: false,
  },
  reducers: {
    setFetchingPriceDetails(state, action: PayloadAction<boolean>) {
      state.isBusy = action.payload;
    },
    setNotifications(
      state,
      action: PayloadAction<BookingProductNotification[]>
    ) {
      state.notifications = action.payload;
    },
    resetPriceDetails(state, _: PayloadAction<void>) {
      state.priceDetails = [];
    },
  },
  extraReducers: (builder) => {
    builder.addCase(fetchPriceDetails.fulfilled, (state, action) => {
      if (action.payload) {
        state.deposit = action.payload.deposit;
        state.priceDetails = action.payload.details;
        state.notifications = action.payload.notifications;
      }
    });
  },
});

export const selectPriceDetails = (state: RootState) =>
  state.priceDetails.priceDetails;

export const selectPackagePriceDetails = createSelector(
  selectPriceDetails,
  (priceDetails) =>
    priceDetails.filter((priceDetail) => priceDetail.isInPackage)
);

export const selectSeparatePackagePriceDetails = createSelector(
  selectPackagePriceDetails,
  (pricedetails) => {
    const result: BookingPriceDetail[] = [];
    const filteredPriceDetails = pricedetails.filter((priceDetail) => priceDetail.isSeparate)

    filteredPriceDetails.forEach(priceDetail => {
      const priceDetailToMerge = result.find(x => x.productCode === priceDetail.productCode && x.accommodationCode === priceDetail.accommodationCode && x.productType === priceDetail.productType);
      if (priceDetailToMerge) {
        priceDetailToMerge.total += priceDetail.total;
        priceDetailToMerge.price += priceDetail.price;
      } else {
        result.push(Object.assign({}, priceDetail));
      }
    });

    return result;
  }
);

export const selectBasePrice = createSelector(
  selectPackagePriceDetails,
  (priceDetails) =>
    sum(
      priceDetails.map((priceDetail) => priceDetail.price * priceDetail.amount)
    )
);

export const selectSeparateExtraPriceDetails = createSelector(
  selectPriceDetails,
  (priceDetails) =>
    priceDetails.filter(
      (priceDetail) => !priceDetail.isInPackage && priceDetail.isSeparate
    )
);

export const selectTotalPrice = createSelector(
  selectBasePrice,
  selectSeparateExtraPriceDetails,
  (basePrice, separatePriceDetails) =>
    sum([
      basePrice,
      ...separatePriceDetails.map(
        (priceDetail) => priceDetail.price * priceDetail.amount
      ),
    ])
);

export const selectDeposit = (state: RootState) => state.priceDetails.deposit;

export const selectIsFetchingPriceDetails = (state: RootState) =>
  state.priceDetails.isBusy;

export const selectNotifications = (state: RootState) =>
  state.priceDetails.notifications;

const { setFetchingPriceDetails } = priceDetailsSlice.actions;

export const { resetPriceDetails, setNotifications } =
  priceDetailsSlice.actions;

export default priceDetailsSlice.reducer;
