import { BadRequestException, Injectable } from '@nestjs/common';
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
import { CreateQuoteDto } from './dto/create-quote.dto';
import { UpdateQuoteDto } from './dto/update-quote.dto';
import { UserDocument } from '../users/schemas/user.schema';
import { Quote, QuoteDocument } from './schemas/quote.schema';
import { Project, ProjectDocument } from '../project/schemas/project.schema';
import { Role } from '../users/roles/role.enum';
import { buildConfigurationPayload } from '../users/hubspot-payload.builder';

@Injectable()
export class QuoteService {
  constructor(
    @InjectModel(Quote.name) private quoteModel: Model<QuoteDocument>,
    @InjectModel(Project.name) private projectModel: Model<ProjectDocument>,
  ) {}

  async create(createQuoteDto: CreateQuoteDto, user: UserDocument) {
    const number = await this.generateNumber(createQuoteDto.number);
    const { order, colors, totalPrice, profile, ...rest } = createQuoteDto;
    const configurationPayload = buildConfigurationPayload({
      project: `${createQuoteDto.project}`,
      profile,
      order,
      colors,
      totalPrice,
      comment: createQuoteDto.comment,
    });
    const quote = await this.quoteModel.create({
      ...rest,
      number,
      client: user._id,
      status: 'Draft',
      configurationPayload,
    });
    await this.projectModel.findOneAndUpdate(
      { _id: createQuoteDto.project },
      { quote: quote._id },
    );

    return (await quote.save()).toObject();
  }

  async createWithoutAuth(createQuoteDto: CreateQuoteDto) {
    const project = await this.projectModel
      .findById(createQuoteDto.project)
      .select('creator')
      .lean();

    if (!project) {
      throw new BadRequestException('Project not found!');
    }

    const number = await this.generateNumber(createQuoteDto.number);
    const { order, colors, totalPrice, profile, ...rest } = createQuoteDto;
    const configurationPayload = buildConfigurationPayload({
      project: `${createQuoteDto.project}`,
      profile,
      order,
      colors,
      totalPrice,
      comment: createQuoteDto.comment,
    });
    const quote = await this.quoteModel.create({
      ...rest,
      number,
      status: 'Draft',
      configurationPayload,
      ...(project.creator ? { client: project.creator } : {}),
    });
    await this.projectModel.findOneAndUpdate(
      { _id: createQuoteDto.project },
      { quote: quote._id },
    );

    return (await quote.save()).toObject();
  }

  findAll(user: UserDocument) {
    let filter = {};
    const populate = [
      {
        path: 'client',
        select: [
          '_id',
          'firstName',
          'lastName',
          'postcode',
          'location',
          'email',
          'phone',
        ],
      },
      {
        path: 'project',
        select: ['_id', 'adminVersion'],
      },
    ];

    if (user.role === Role.User) {
      filter = {
        client: user._id,
      };
    }

    return this.quoteModel
      .find(filter)
      .select('-__v -updatedAt')
      .populate(populate)
      .sort({ createdAt: -1 })
      .exec();
  }

  findOne(_id: string) {
    return this.quoteModel.find({ _id }).exec();
  }

  async update(_id: string, updateQuoteDto: UpdateQuoteDto, siteUrl) {
    const { order, colors, totalPrice, profile, project, ...rest } =
      updateQuoteDto;

    const updates: any = { ...rest, revised: true };

    const hasOrderInputs =
      order !== undefined ||
      colors !== undefined ||
      totalPrice !== undefined ||
      profile !== undefined;

    if (hasOrderInputs) {
      updates.configurationPayload = buildConfigurationPayload({
        project: project ? `${project}` : undefined,
        profile,
        order,
        colors,
        totalPrice,
        comment: rest.comment,
      });
    }

    const newQuote: any = await this.quoteModel
      .findOneAndUpdate({ _id }, updates, { new: true })
      .populate([
        {
          path: 'client',
          select: [
            '_id',
            'firstName',
            'lastName',
            'postcode',
            'location',
            'email',
            'phone',
          ],
        },
      ])
      .exec();
    if (!newQuote) {
      throw new BadRequestException('Quote not found!');
    }

    return newQuote;
  }

  async remove(_id: string) {
    await this.quoteModel.remove({ _id });
    return { success: true };
  }

  async generateNumber(n?: number) {
    const number = n ?? Math.floor(100000 + Math.random() * 9000000);
    const quote = await this.quoteModel.findOne({ number });

    if (quote) {
      return this.generateNumber();
    }

    return number;
  }
}
