"use strict";
const db = require("../../models/index");
const sequelize = require("sequelize");
const Op = sequelize.Op;
const { AD_CATEGORY } = require("../../common/enums");

const getSearchRequest = async searchRequestId => {
  try {
    return await db.SearchRequest.findByPk(searchRequestId);
  } catch (error) {
    console.log("searchrequest.js", error);
    return null;
  }
};

const createSearchRequest = async (searchRequestFields = {}) => {
  return await db.SearchRequest.create(searchRequestFields);
};

const findSearchRequestsForRealEstate = async realEstate => {
  const {
    price,
    area,
    adType,
    realEstateType,
    locationLat,
    locationLong,
    accessRoadType,
    balcony,
    newBuilding,
    elevator,
    gardenSize,
    numberOfRooms,
    numberOfFloors,
    floor
  } = realEstate;

  if (!locationLat || !locationLong) {
    return [];
  }

  const stGeometry = sequelize.fn(
    "ST_GEOMFROMTEXT",
    `POINT (${locationLong} ${locationLat})`,
    4326
  );
  const areaToSearchColumn = sequelize.col("areaToSearch");
  const contains = sequelize.fn("ST_Contains", areaToSearchColumn, stGeometry);

  const geoSearchQueryPart = sequelize.where(contains, true);

  //Needed for defining which attribute should exist or not
  const realEstateTypeObject = AD_CATEGORY[realEstateType];

  // ?? Needed to decide on including incomplete RealEstates data
  let checkForIncompleteWanted = false;

  //Attributes are checked separately to make different query parts

  //If real estate price is number then it searches for req that have priceMin and priceMax
  //If real estate price is null it searches for req that accept ads without price
  //User always defines price and area (sliders) - not null in search req
  let priceQuery = {};
  if (price != null) {
    priceQuery = {
      [Op.and]: [
        {
          priceMin: {
            [Op.lte]: price
          }
        },
        {
          priceMax: {
            [Op.gte]: price
          }
        }
      ]
    };
  } else {
    priceQuery = {
      includeWithoutPrice: {
        [Op.eq]: true
      }
    };
  }

  let areaQuery = {};
  if (area != null) {
    areaQuery = {
      [Op.and]: [
        {
          sizeMin: {
            [Op.lte]: area
          }
        },
        {
          sizeMax: {
            [Op.gte]: area
          }
        }
      ]
    };
  } else {
    checkForIncompleteWanted = true;
  }

  //Other attributes can be defined or not depending on RealEstate type
  //we check what to include in query based on real estate type object
  let gardenSizeQuery = {};
  if (realEstateTypeObject.hasGardenSize) {
    if (gardenSize != null) {
      gardenSizeQuery = {
        [Op.and]: [
          {
            gardenSizeMin: {
              [Op.lte]: gardenSize
            }
          },
          {
            gardenSizeMax: {
              [Op.gte]: gardenSize
            }
          }
        ]
      };
    } else {
      checkForIncompleteWanted = true;
    }
  }

  let numberOfRoomsQuery = {};
  if (realEstateTypeObject.hasNumberOfRoom) {
    if (numberOfRooms != null) {
      //If real estate has defined number of rooms ex. 3 it returns req
      // that accepts 3 rooms or ones that don't have defined number - null
      //Ex. they didnt choose advanced filters at all
      numberOfRoomsQuery = {
        [Op.and]: [
          {
            numberOfRoomsMin: {
              [Op.or]: {
                [Op.lte]: numberOfRooms,
                [Op.is]: null
              }
            }
          },
          {
            numberOfRoomsMax: {
              [Op.or]: {
                [Op.gte]: numberOfRooms,
                [Op.is]: null
              }
            }
          }
        ]
      };
    } else {
      // If real estate dont have defined number of rooms ex. null
      //It returns requests that didn't choose number of rooms - also null
      //Or ones that picked some values but also picked to includeIncomplete ads
      numberOfRoomsQuery = {
        [Op.or]: [
          {
            [Op.and]: [
              {
                numberOfRoomsMin: {
                  [Op.is]: null
                }
              },
              {
                numberOfRoomsMax: {
                  [Op.is]: null
                }
              }
            ]
          },
          {
            includeIncompleteAds: {
              [Op.eq]: true
            }
          }
        ]
      };
    }
  }
  //Same logic for number of Floors and floors
  let numberOfFloorsQuery = {};
  if (realEstateTypeObject.hasNumberOfFloors) {
    if (numberOfFloors != null) {
      numberOfFloorsQuery = {
        [Op.and]: [
          {
            numberOfFloorsMin: {
              [Op.or]: {
                [Op.lte]: numberOfFloors,
                [Op.is]: null
              }
            }
          },
          {
            numberOfFloorsMax: {
              [Op.or]: {
                [Op.gte]: numberOfFloors,
                [Op.is]: null
              }
            }
          }
        ]
      };
    } else {
      numberOfFloorsQuery = {
        [Op.or]: [
          {
            [Op.and]: [
              {
                numberOfFloorsMin: {
                  [Op.is]: null
                }
              },
              {
                numberOfFloorsMax: {
                  [Op.is]: null
                }
              }
            ]
          },
          {
            includeIncompleteAds: {
              [Op.eq]: true
            }
          }
        ]
      };
    }
  }
  let floorQuery = {};
  if (realEstateTypeObject.hasFloorProp) {
    if (floor != null) {
      floorQuery = {
        [Op.and]: [
          {
            floorMin: {
              [Op.or]: {
                [Op.lte]: floor,
                [Op.is]: null
              }
            }
          },
          {
            floorMax: {
              [Op.or]: {
                [Op.gte]: floor,
                [Op.is]: null
              }
            }
          }
        ]
      };
    } else {
      floorQuery = {
        [Op.or]: [
          {
            [Op.and]: [
              {
                floorMin: {
                  [Op.is]: null
                }
              },
              {
                floorMax: {
                  [Op.is]: null
                }
              }
            ]
          },
          {
            includeIncompleteAds: {
              [Op.eq]: true
            }
          }
        ]
      };
    }
  }

  //Logic for balcony, newBuilding and elevator
  //If user dont check checkbox for ex. elevator  it does not mean he only wants no elevator
  //If real estate characteristic =true find all req, one that wants charachertistic or dont care - dont need query
  //If real estate characteristic = false, find all req exept for ones that wants characteristic to be true
  //If real estate characteristic = null, dont know if true or false, find req that dont care or want char and want incomplete ads
  let balconyQuery = {};
  if (realEstateTypeObject.hasBalconyProp && balcony !== true) {
    if (balcony === false) {
      balconyQuery = {
        balcony: {
          [Op.ne]: true
        }
      };
    } else if (balcony === null) {
      balconyQuery = {
        [Op.or]: [
          {
            balcony: {
              [Op.ne]: true
            }
          },
          {
            [Op.and]: [
              {
                balcony: {
                  [Op.eq]: true
                }
              },
              {
                includeIncompleteAds: {
                  [Op.eq]: true
                }
              }
            ]
          }
        ]
      };
    }
  }
  let newBuildingQuery = {};
  if (realEstateTypeObject.hasNewBuildingProp && newBuilding !== true) {
    if (newBuilding === false) {
      newBuildingQuery = {
        newBuilding: {
          [Op.ne]: true
        }
      };
    } else if (newBuilding === null) {
      newBuildingQuery = {
        [Op.or]: [
          {
            newBuilding: {
              [Op.ne]: true
            }
          },
          {
            [Op.and]: [
              {
                newBuilding: {
                  [Op.eq]: true
                }
              },
              {
                includeIncompleteAds: {
                  [Op.eq]: true
                }
              }
            ]
          }
        ]
      };
    }
  }
  let elevatorQuery = {};
  if (realEstateTypeObject.hasElevatorProp && elevator !== true) {
    if (elevator === false) {
      elevatorQuery = {
        elevator: {
          [Op.ne]: true
        }
      };
    } else if (elevator === null) {
      elevatorQuery = {
        [Op.or]: [
          {
            elevator: {
              [Op.ne]: true
            }
          },
          {
            [Op.and]: [
              {
                elevator: {
                  [Op.eq]: true
                }
              },
              {
                includeIncompleteAds: {
                  [Op.eq]: true
                }
              }
            ]
          }
        ]
      };
    }
  }
  //General query consists of each individual query
  const query = {
    adType,
    realEstateType,
    subscribed: true,
    [Op.and]: [
      geoSearchQueryPart,
      priceQuery,
      areaQuery,
      gardenSizeQuery,
      numberOfRoomsQuery,
      numberOfFloorsQuery,
      floorQuery,
      balconyQuery,
      newBuildingQuery,
      elevatorQuery
    ]
  };

  //AccessRoadType is defined - should exists for each ad and estate type
  if (accessRoadType != null) {
    query.accessRoadType = {
      [Op.or]: {
        [Op.like]: "ANY",
        [Op.eq]: accessRoadType
      }
    };
  } else {
    //Null values are returned for user request that wanted ANY acces road type
    query.accessRoadType = {
      [Op.eq]: "ANY"
    };
  }
  //Tag to check if incomplete ads are accepted in query
  if (checkForIncompleteWanted) {
    query.includeIncompleteAds = {
      [Op.eq]: true
    };
  }

  return await db.SearchRequest.findAll({
    where: query
  });
};

module.exports = {
  getSearchRequest,
  createSearchRequest,
  findSearchRequestsForRealEstate
};
