import { ConflictException, Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { HttpService } from '@nestjs/axios';
import { Cron } from '@nestjs/schedule';
import { User, UserDocument } from './schemas/user.schema';
import { Model } from 'mongoose';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import * as bcrypt from 'bcrypt';
import { Role } from './roles/role.enum';
import { ResetPasswordDto } from './dto/reset-password.dto';
import { MailService } from '../mail/mail.service';
import { ConfigService } from '@nestjs/config';
import { ChangePasswordDto } from './dto/change-password.dto';
import { Token, TokenDocument } from './schemas/token.schema';
import { TemplateResetPasswordMail } from '../mail/template/resetPassword';
import { AppMailerService } from './../mailer/mailer.service';

@Injectable()
export class UsersService {
  constructor(
    @InjectModel(User.name) private userModel: Model<UserDocument>,
    @InjectModel(Token.name) private readonly tokenModel: Model<TokenDocument>,
    private readonly mailService: MailService,
    private readonly appMailerServices: AppMailerService,
    private readonly httpService: HttpService,
    private readonly configService: ConfigService,
  ) {}

  async create(dto: CreateUserDto): Promise<User> {
    const existing = await this.userModel.findOne({ email: dto.email });

    if (existing && existing.password) {
      throw new ConflictException('User already exist');
    }

    let userNew: User;

    if (existing) {
      const upgraded = await this.userModel
        .findOneAndUpdate(
          { _id: existing._id },
          { ...dto, role: Role.User },
          { new: true },
        )
        .lean();
      userNew = upgraded as User;
    } else {
      const user = new this.userModel({ ...dto, role: Role.User });
      userNew = (await user.save()).toObject();
    }

    try {
      const msg = {
        to: userNew.email,
        from: 'sales@thermogreen.ch',
        bcc: ['Daniel.Tiso@thermogreen.ch', 'info@thermogreen.ch'],
        subject: 'Thermogreen',
        html: 'Hallo!<br/>Sie haben sich erfolgreich bei Thermogreen registriert, vielen Dank, dass Sie sich dafür entschieden haben.',
      };
      await this.mailService.send(msg);

      // const msg = {
      //   to: userNew.email,
      //   subject: 'Ihr Passwort für den Thermogreen-Konfigurator',
      //   html: 'Hallo!<br/>Sie haben sich erfolgreich bei Thermogreen registriert, vielen Dank, dass Sie sich dafür entschieden haben.',
      // };

      // await this.appMailerServices.send(msg);
    } catch (e) {
      console.log(e);
    }

    return userNew;
  }

  async update(u: User, dto: UpdateUserDto): Promise<User> {
    const user = await this.userModel.findOne({ email: u.email });

    if (!user) {
      throw new ConflictException('User not found');
    }

    const isNotUniqueEmail = dto.email
      ? await this.userModel.findOne({ email: dto.email })
      : false;

    if (isNotUniqueEmail) {
      throw new ConflictException('The user from this email already exists');
    }

    if (dto.password) {
      dto.password = await this.hashPassword(dto.password);
    }

    return this.userModel
      .findOneAndUpdate({ email: u.email }, dto, { new: true })
      .select('-password -createdAt -updatedAt -__v')
      .lean();
  }

  async resetPassword(dto: ResetPasswordDto) {
    const token = await this.tokenModel.findOne({ token: dto.token });

    if (!token) {
      throw new ConflictException('Token not found');
    }

    return this.userModel
      .findOneAndUpdate(
        { _id: token.user },
        { password: await this.hashPassword(dto.password) },
      )
      .select('-password -createdAt -updatedAt -__v');
  }

  async changePassword(dto: ChangePasswordDto, u: User) {
    const user = await this.userModel.findOne({ email: u.email });

    if (!user) {
      throw new ConflictException('User not found');
    }

    return this.userModel
      .findOneAndUpdate(
        { email: u.email },
        { password: await this.hashPassword(dto.password) },
      )
      .select('-password -createdAt -updatedAt -__v');
  }

  async findAll(query: any): Promise<UserDocument[]> {
    return this.userModel.find(query);
  }

  async findOne(email: string): Promise<UserDocument> {
    const user = await this.userModel.findOne({ email }).exec();

    if (!user) {
      throw new ConflictException('User not found');
    }

    return user;
  }

  async isEmailRegistered(email: string): Promise<boolean> {
    if (!email) return false;
    const user = await this.userModel
      .findOne({ email: email.trim().toLowerCase() })
      .select('password')
      .lean();
    return Boolean(user?.password);
  }

  async isValidPassword(password: string, userPassword: string) {
    return await bcrypt.compare(password, userPassword);
  }

  async hashPassword(password: string) {
    return await bcrypt.hash(password, 12);
  }

  async postDelivery(data: { postcode: string }) {
    const key = this.configService.get('DELIVERI_KEY');
    const response = await this.httpService.axiosRef.get(
      `https://maps.googleapis.com/maps/api/distancematrix/json`,
      {
        params: {
          origins: 'Kaiserstuhlstrasse 2, 8154 Oberglatt, Switzerland',
          destinations: `${data.postcode}, Switzerland`,
          key: key,
        },
      },
    );

    return response.data;
  }

  async sendResetPasswordMail(token: string, email: string) {
    const user = await this.userModel.findOne({ email });
    if (!user) {
      throw new ConflictException('User not found');
    }

    const expirationDate = new Date();
    expirationDate.setMinutes(expirationDate.getMinutes() + 60);

    const newToken = new this.tokenModel({
      user: user._id,
      token,
      expirationDate,
    });

    await newToken.save();

    try {
      const msg = {
        to: email,
        from: 'sales@thermogreen.ch',
        subject: 'Ihr Passwort für den Thermogreen-Konfigurator',
        html: TemplateResetPasswordMail({
          link: `${
            this.configService.get('SERVER_ADDRESS') || ''
          }/?token=${token}`,
        }),
      };
      await this.mailService.send(msg);

      // const msg = {
      //   to: email,
      //   subject: 'Ihr Passwort für den Thermogreen-Konfigurator',
      //   html: TemplateResetPasswordMail({
      //     link: `${
      //       this.configService.get('SERVER_ADDRESS') || ''
      //     }/?token=${token}`,
      //   }),
      // };

      // await this.appMailerServices.send(msg);
    } catch (e) {
      console.log(e);
    }
  }

  @Cron('0 * * * *')
  async deleteExpiredTokens() {
    const currentDate = new Date();
    await this.tokenModel.deleteMany({ expirationDate: { $lte: currentDate } });
  }
}
