import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Route, RouteStatus } from './entities/route.entity';
import { DataSource, Repository } from 'typeorm';
import { User } from 'src/user/entities/user.entity';
import { Town } from 'src/town/entities/town.entity';
import { TravelPlaceService } from 'src/travel-place/travel-place.service';
import { RecommendationsSystem } from './utils/recommendations';
import { PlaceService } from 'src/place/place.service';
import { LANGUAGES } from 'src/shared/enum/languages.enum';
import { VisitedService } from 'src/visited/visited.service';
import { GetPlaceDto } from 'src/place/dto/get-place.dto';
import { Visited } from 'src/visited/entities/visited.entity';
import { RecommendPlace } from './dto/recommend-route.dto';
import { CreateRouteDto } from './dto/create-route.dto';
import { CreateTravelPlaceDto } from 'src/travel-place/dto/create-travel-place.dto';
import { UserService } from 'src/user/user.service';
import { TravelPlace } from 'src/travel-place/entities/travel-place.entity';
import { ServerConstants } from 'src/constants/server.contants';

@Injectable()
export class RouteService {
  constructor(
    @InjectRepository(Route) private routeRepository: Repository<Route>,
    @InjectRepository(User) private userRepository: Repository<User>,
    @InjectRepository(Town) private townRepository: Repository<Town>,
    private readonly travelPlaceService: TravelPlaceService,
    private readonly placeService: PlaceService,
    private readonly visitedService: VisitedService,
    private readonly userService: UserService,
    private dataSource: DataSource,
  ) {}

  async recommend(idTown: number, email: string, language: LANGUAGES, start, end) {
    // Obtener los visitados y los candidatos
    const town: Town = await this.townRepository.findOneBy({ townId: idTown });
    const user: User = await this.userRepository.findOneBy({ email });
    const placesNotVisited: GetPlaceDto[] = await this.placeService.findPlacesNotVisitedByUserAndOpen(
      email,
      language,
      idTown,
    );
    const visited: Visited[] = await this.visitedService.getVisitedByUser(email);
    const placesMapped: RecommendPlace[] = placesNotVisited.map((place) => {
      return {
        idPlace: place.idPlace,
        openAt: place.openAt,
        closeAt: place.closeAt,
        categories: place.categories.map((category) => category.idCategory),
        rating: 0,
      };
    });

    const visitedMapped: RecommendPlace[] = visited.map((visit) => ({
      idPlace: visit.place.idPlace,
      openAt: visit.place.openAt,
      closeAt: visit.place.closeAt,
      categories: visit.place.categories.map((category) => category.idCategory),
      rating: visit.rating,
    }));

    const system = new RecommendationsSystem(visitedMapped, placesMapped, start, end);
    const chosen: RecommendPlace[] = system.recommend();

    const startDate: Date = new Date();
    startDate.setHours(start);
    const endDate = new Date();
    endDate.setHours(end);

    const createRouteDto: CreateRouteDto = { startDate, endDate, town, user };
    const idRoute = (await this.routeRepository.save(createRouteDto)).idRoute;

    for (const curRecommended of chosen) {
      const place = await this.placeService.findOneAndTradAndAvailable(curRecommended.idPlace, language);
      const endDate = new Date();
      endDate.setHours(curRecommended.closeAt);
      endDate.setMinutes(0);
      const startDate = new Date();
      startDate.setHours(curRecommended.openAt);
      startDate.setMinutes(0);

      const createTravelPlace: CreateTravelPlaceDto = {
        ...place,
        done: false,
        idRoute,
        endDate,
        startDate,
      };

      await this.travelPlaceService.create(createTravelPlace);
    }

    return await this.getRouteById(idRoute, email, language);
  }

  async getRouteAndPlacesByUser(email: string, routeStatus: RouteStatus) {
    const res = await this.routeRepository.find({
      relations: ['travelPlace'],
      where: { user: { email }, status: routeStatus },
      order: { endDate: 'DESC' },
    });

    return res;
  }

  async updateRoute(idRoute: number, status: RouteStatus) {
    await this.routeRepository.update({ idRoute }, { status });
  }

  async getRouteById(idRoute: number, email: string, lang: LANGUAGES) {
    const user: User = await this.userService.findOne(email);
    const res = await this.routeRepository.findOneBy({ idRoute, user });

    if (res && res.travelPlace) {
      res.travelPlace = res.travelPlace.map((place) => {
        place.place.categories = place.place.categories.filter((category) => category.language === lang);
        return place;
      });

      res.travelPlace = res.travelPlace.map((travelPlace: TravelPlace) => {
        return {
          travelPlaceId: travelPlace.travelPlaceId,
          idPlace: travelPlace.place.idPlace,
          available: travelPlace.place.available,
          latitude: travelPlace.place.latitude,
          longitude: travelPlace.place.longitude,
          imageName: `${ServerConstants.HOST}/places/${travelPlace.place.imageName}`,
          name: travelPlace.place.name,
          openAt: travelPlace.place.openAt,
          closeAt: travelPlace.place.closeAt,
          startDate: travelPlace.startDate,
          endDate: travelPlace.endDate,
          categories: travelPlace.place.categories,
          address: travelPlace.place.address,
          done: travelPlace.done,
          route: travelPlace.route,
          place: { ...travelPlace.place, imageName: `${ServerConstants.HOST}/places/${travelPlace.place.imageName}` },
        };
      });
    }

    console.log(res);

    return res;
  }

  async getRouteInfoById(idRoute: number) {
    const res = await this.dataSource
      .createQueryBuilder(Route, 'route')
      .leftJoinAndSelect('route.travelPlace', 'travelPlace')
      .leftJoinAndSelect('travelPlace.place', 'place')
      .leftJoinAndSelect('place.visited', 'visited')
      .leftJoinAndSelect('route.town', 'town')
      .where('route.idRoute = :idRoute', { idRoute })
      .getOne();

    if (res && res.travelPlace) {
      res.travelPlace = res.travelPlace.map((travelPlace: TravelPlace) => {
        return {
          travelPlaceId: travelPlace.travelPlaceId,
          idPlace: travelPlace.place.idPlace,
          available: travelPlace.place.available,
          latitude: travelPlace.place.latitude,
          longitude: travelPlace.place.longitude,
          imageName: `${ServerConstants.HOST}/places/${travelPlace.place.imageName}`,
          name: travelPlace.place.name,
          openAt: travelPlace.place.openAt,
          closeAt: travelPlace.place.closeAt,
          startDate: travelPlace.startDate,
          endDate: travelPlace.endDate,
          categories: travelPlace.place.categories,
          address: travelPlace.place.address,
          done: travelPlace.done,
          route: travelPlace.route,
          place: { ...travelPlace.place, imageName: `${ServerConstants.HOST}/places/${travelPlace.place.imageName}` },
        };
      });
    }

    return res;
  }
}
