import { Injectable } 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 {
  CreateProjectDto,
  FunctionDto,
  ProjectDto,
  UpdateProjectDto,
} from "@sigma/common";

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

  async create(
    owner: ObjectId,
    createProjectDto: CreateProjectDto
  ): Promise<ProjectDocument> {
    const createdProject = new this.projectModel({
      ...createProjectDto,
      owner,
    });

    return createdProject.save();
  }

  async findAll(
    owner: ObjectId,
    query: UpdateProjectDto
  ): Promise<ProjectDocument[]> {
    return this.projectModel
      .find({
        owner,
        ...query,
      })
      .exec();
  }

  async findOne(owner: ObjectId, id: string): Promise<ProjectDto | null> {
    const project = await this.projectModel
      .findOne({
        _id: id,
        owner,
      })
      .exec();

    if (!project) {
      return null;
    }

    const functions = await this.functionModel
      .find({ project: project._id })
      .exec();

    return this.toProjectDto(project, functions);
  }

  async update(
    owner: ObjectId,
    id: string,
    updateProjectDto: UpdateProjectDto
  ): Promise<ProjectDocument | null> {
    return this.projectModel
      .findOneAndUpdate(
        {
          _id: id,
          owner,
        },
        updateProjectDto,
        { new: true }
      )
      .exec();
  }

  async remove(owner: ObjectId, id: string): Promise<ProjectDocument | null> {
    return this.projectModel
      .findOneAndDelete({
        _id: id,
        owner,
      })
      .exec();
  }

  private toProjectDto(
    project: ProjectDocument,
    functions: FunctionDocument[]
  ): ProjectDto {
    return {
      _id: project._id.toString(),
      name: project.name,
      slug: project.slug,
      description: project.description,
      cpuTimeQuotaMsPerMinute: project.cpuTimeQuotaMsPerMinute ?? 0,
      cpuTimeWindowStartedAt:
        project.cpuTimeWindowStartedAt?.toISOString() ??
        new Date(0).toISOString(),
      cpuTimeUsedMs: project.cpuTimeUsedMs ?? 0,
      functionsInvocationCount: project.functionsInvocationCount ?? 0,
      functionsErrorCount: project.functionsErrorCount ?? 0,
      functions: functions.map((func) => this.toFunctionDto(func)),
    };
  }

  private toFunctionDto(func: FunctionDocument): FunctionDto {
    return {
      _id: func._id.toString(),
      name: func.name,
      path: func.path,
      methods: func.methods ?? [],
      code: func.code,
      invocations: func.invocationCount ?? 0,
      errors: func.errorCount ?? 0,
      logs: func.logs ?? [],
      lastInvocation: func.lastInvocation,
    };
  }
}
