import {
  BadGatewayException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { RetrievePhysicalActivitiesResponseDto } from './dtos/retrieve-physical-activities.dto';
import {
  RetrievePhysicalActivityParamsDto,
  RetrievePhysicalActivityResponseDto,
} from './dtos/retrieve-physical-activity.dto';
import { RetrieveGoalsResponseDto } from './dtos/retrieve-goals.dto';
import {
  RetrieveGoalParamsDto,
  RetrieveGoalResponseDto,
} from './dtos/retrieve-goal.dto';
import { RetrieveAnyUserResponseDto } from './dtos/retrieve-any-user.dto';
import { SingleIdParamsDto } from '../../shared/dtos/params/single-id-params.dto';
import { CloudinaryService } from '../../services/image-management/cloudinary/cloudinary.service';
import { SingleUsernameDto } from '../../shared/dtos/users/single-username.dto';
import { SimpleNumberDto } from '../../shared/dtos/simple-number.dto';

@Injectable()
export class UsersService {
  constructor(
    private readonly _entityManager: EntityManager,
    private readonly _cloudinaryService: CloudinaryService,
  ) {}

  async retrieveUsersCount(): Promise<SimpleNumberDto> {
    const SQL = 'SELECT COUNT(*) AS count FROM user';

    const [{ count }] = await this._entityManager.query(SQL).catch(() => {
      throw new BadGatewayException();
    });

    return { data: Number(count) };
  }

  private async _retrieveAnyUserBy(
    WHERE: string,
    parameters: any[],
  ): Promise<RetrieveAnyUserResponseDto> {
    const SQL = `
      SELECT
        person.id AS id,
        person.full_name AS fullName,
        user.username AS username,
        user.profile_picture_path AS profilePicturePath
      FROM person
      INNER JOIN user
        ON person.id = user.person_id
      ${WHERE}
    `;

    const [user] = await this._entityManager
      .query(SQL, parameters)
      .catch(() => {
        throw new BadGatewayException();
      });

    if (!user) throw new NotFoundException();

    if (user.profilePicturePath) {
      user.profilePictureUrl = `${this._cloudinaryService.cloudUrl}/${user.profilePicturePath}`;
    }

    delete user.profilePicturePath;

    return { data: user };
  }

  async retrieveAnyUser(
    params: SingleIdParamsDto,
  ): Promise<RetrieveAnyUserResponseDto> {
    const { id } = params;

    const WHERE = 'WHERE person.id = ?';

    return this._retrieveAnyUserBy(WHERE, [id]);
  }

  async retrieveAnyUserByUsername(
    params: SingleUsernameDto,
  ): Promise<RetrieveAnyUserResponseDto> {
    const { username } = params;

    const WHERE = 'WHERE user.username = ?';

    return this._retrieveAnyUserBy(WHERE, [username]);
  }

  async retrievePhysicalActivities(): Promise<RetrievePhysicalActivitiesResponseDto> {
    const PHYSICAL_ACTIVITIES_SQL =
      'SELECT id, label, description, tdee_multiplier AS tdeeMultiplier FROM physical_activity';

    const physicalActivities = await this._entityManager
      .query(PHYSICAL_ACTIVITIES_SQL)
      .catch(() => {
        throw new BadGatewayException();
      });

    return {
      data: physicalActivities,
    };
  }

  async retrievePhysicalActivity(
    params: RetrievePhysicalActivityParamsDto,
  ): Promise<RetrievePhysicalActivityResponseDto> {
    const { id } = params;

    const PHYSICAL_ACTIVITY_SQL =
      'SELECT id, label, description, tdee_multiplier AS tdeeMultiplier FROM physical_activity WHERE id = ?';

    const [physicalActivity] = await this._entityManager
      .query(PHYSICAL_ACTIVITY_SQL, [id])
      .catch(() => {
        throw new BadGatewayException();
      });

    if (!physicalActivity) throw new NotFoundException();

    return {
      data: physicalActivity,
    };
  }

  async retrieveGoals(): Promise<RetrieveGoalsResponseDto> {
    const GOALS_SQL = 'SELECT id, label, tdee_offset AS bmrOffset FROM goal';

    const goals = await this._entityManager.query(GOALS_SQL).catch(() => {
      throw new BadGatewayException();
    });

    return {
      data: goals,
    };
  }

  async retrieveGoal(
    params: RetrieveGoalParamsDto,
  ): Promise<RetrieveGoalResponseDto> {
    const { id } = params;

    const GOAL_SQL =
      'SELECT id, label, tdee_offset AS bmrOffset FROM goal WHERE id = ?';

    const [goal] = await this._entityManager.query(GOAL_SQL, [id]).catch(() => {
      throw new BadGatewayException();
    });

    if (!goal) throw new NotFoundException();

    return {
      data: goal,
    };
  }
}
