import { Injectable, NotFoundException } from "@nestjs/common";
import { InjectModel } from "@nestjs/mongoose";
import type { Model, ObjectId } from "mongoose";
import { Project, type ProjectDocument } from "../schemas/project.schema.js";
import {
  Function as SigmaFunction,
  type FunctionDocument,
} from "../schemas/function.schema.js";
import { CreateFunctionDto, UpdateFunctionDto } from "@sigma/common";

@Injectable()
export class ProjectFunctionsService {
  constructor(
    @InjectModel(Project.name)
    private readonly projectModel: Model<Project>,
    @InjectModel(SigmaFunction.name)
    private readonly functionModel: Model<SigmaFunction>
  ) {}

  async create(
    owner: ObjectId,
    projectId: string,
    payload: CreateFunctionDto
  ): Promise<FunctionDocument> {
    const project = await this.projectModel
      .findOne({
        _id: projectId,
        owner,
      })
      .exec();

    if (!project) {
      throw new NotFoundException("Project not found");
    }

    const createdFunction = await this.functionModel.create({
      project: project._id,
      name: payload.name,
      path: payload.path,
    });

    await this.appendFunctionReference(project, createdFunction._id);

    return createdFunction;
  }

  async delete(
    owner: ObjectId,
    projectId: string,
    functionId: string
  ): Promise<FunctionDocument | null> {
    const project = await this.projectModel.findOne({
      _id: projectId,
      owner,
    });

    if (!project) {
      throw new NotFoundException("Project not found");
    }

    const func = await this.functionModel.findOneAndDelete({
      project: project._id,
      _id: functionId,
    });

    await project.updateOne(
      {
        functions: project.functions.filter(
          (funcId) => funcId.toString() !== functionId
        ),
      },
      { new: true }
    );

    return func;
  }

  async update(
    owner: ObjectId,
    projectId: string,
    functionId: string,
    payload: UpdateFunctionDto
  ): Promise<FunctionDocument | null> {
    const project = await this.projectModel.findOne({
      _id: projectId,
      owner,
    });

    if (!project) {
      throw new NotFoundException("Project not found");
    }

    return await this.functionModel.findOneAndUpdate(
      {
        project: project._id,
        _id: functionId,
      },
      payload,
      { new: true }
    );
  }

  private async appendFunctionReference(
    project: ProjectDocument,
    functionId: FunctionDocument["_id"]
  ): Promise<void> {
    await this.projectModel
      .updateOne({ _id: project._id }, { $addToSet: { functions: functionId } })
      .exec();
  }
}
