/home/bdqbpbxa/dev-subdomains/admin.pixory.goodface.com.ua/src/api/cart/controllers/cart.ts
import { factories } from '@strapi/strapi'
import { Context } from 'koa'
import { ValidationError } from 'yup';
import { setCartSchema } from '../common/schemas/set-cart';
import { addCartSchema } from '../common/schemas/add-cart';
import { SetCart } from '../common/types/set-cart';
import { ProjectService } from 'src/api/project/services/project';
import { AddCart } from '../common/types/add-cart';
import { Project } from 'src/api/project/common/types/project';
import { BookTypeService } from 'src/api/book-type/services/book-type';
import { BookSizeService } from 'src/api/book-size/services/book-size';
import { PaperFinishService } from 'src/api/paper-finish/services/paper-finish';
import { CartService } from '../services/cart';
import { OrderedProjectService } from 'src/api/ordered-project/services/ordered-project';

export default factories.createCoreController('api::cart.cart', ({ strapi }) => ({
  async find(ctx: Context) {
    if (!ctx.state.user) {
      return ctx.unauthorized('You must be authenticated to update user information');
    }

    const cartService = new CartService(strapi);
    const cart = await cartService.findOrCreate(ctx.state.user.id);

    return ctx.send(cart);
  },

  async add(ctx: Context) {
    try {
      if (!ctx.state.user) {
        return ctx.unauthorized('You must be authenticated to update user information');
      }

      addCartSchema.validateSync(ctx.request?.body || {});

      const { projectId } = ctx.request.body as AddCart;

      const cartService = new CartService(strapi);
      const projectService = new ProjectService(strapi);
      const projectData = await projectService.getProjectData(projectId);

      const {bookType, bookSize, paperFinish} = await cartService.getBookAttributes(projectData);

      if (!bookType || !bookSize || !paperFinish) {
        strapi.log.error(`Project attributes are not fully mapped in the system. Project data: ${projectData}`);
        return ctx.badRequest('Project attributes are not fully mapped in the system. Contact support.');
      }

      const cart = await cartService.findOrCreate(ctx.state.user.id);

      let project = await strapi.db.query('api::project.project').findOne({
        where: { printboxProjectUuid: projectData.uuid }
      }) as Project;

      if (!project) {
        project = await strapi.db.query('api::project.project').create({
          data: {
            cart: cart.id,
            user: ctx.state.user.id,
            title: projectData.name,
            printboxProjectUuid: projectData.uuid,
            priceDiscounted: projectData.params[0].price_net,
            price: projectData.params[0].price_gross,
            bookType: bookType?.id,
            bookSize: bookSize?.id,
            paperFinish: paperFinish?.id,
          },
          populate: {
            bookType: true,
            bookSize: true,
            paperFinish: true,
          }
        }) as Project;
      }

      const existingOrderedProject = await strapi.db.query('api::ordered-project.ordered-project').findOne({
        where: { project: project.id }
      });

      const orderedProjectService = new OrderedProjectService(strapi);
      if (existingOrderedProject) {
        await orderedProjectService.increment(existingOrderedProject.id, existingOrderedProject.quantity);
      } else {
        const orderedProject = await orderedProjectService.create(project.id, cart.id);
        await cartService.appendOrderedProject(cart.id, [orderedProject.id]);
      }

      ctx.send(await cartService.findOrCreate(ctx.state.user.id));
    } catch (error) {
      if (error instanceof ValidationError) {
        return ctx.badRequest('Validation error', error.errors);
      }

      strapi.log.error('Error updating user:', error);
      return ctx.internalServerError('An error occurred while updating the user');
    }
  },

  async set(ctx: Context) {
    try {
      if (!ctx.state.user) {
        return ctx.unauthorized('You must be authenticated to update user information');
      }

      setCartSchema.validateSync(ctx.request.body || {});

      let { projects } = ctx.request.body as SetCart;

      projects = projects.reduce((acc, project) => {
        const existingProject = acc.find(p => p.projectId === project.projectId);
        if (existingProject) {
          existingProject.quantity += project.quantity;
        } else {
          acc.push({ ...project });
        }
        return acc;
      }, [] as { projectId: string; quantity: number }[]);

      const cartService = new CartService(strapi);
      const cart = await cartService.findOrCreate(ctx.state.user.id);

      const projectService = new ProjectService(strapi);
      let projectsPrintboxData = await projectService.getProjectsData(projects.map(p => p.projectId));

      const nonEmptyProjectData = projectsPrintboxData.filter(p => p !== null);

      const existingProjects = await strapi.db.query('api::project.project').findMany({
        where: { printboxProjectUuid: { $in: projectsPrintboxData.map(p => p.uuid) } }
      });
      const existingProjectsUuids = existingProjects.map(p => p.printboxProjectUuid);
      const projectsDataToCreate = nonEmptyProjectData.filter(p => !existingProjectsUuids.includes(p.uuid));

      const projectsData = await Promise.all(projectsDataToCreate.map(async (project) => {
        const {bookType, bookSize, paperFinish} = await cartService.getBookAttributes(project);
        if (!bookType || !bookSize || !paperFinish) {
          strapi.log.error(`Project attributes are not fully mapped in the system. Project data: ${project}`);
          return ctx.badRequest('Project attributes are not fully mapped in the system. Contact support.');
        }

        return {
          cart: cart.id,
          user: ctx.state.user.id,
          title: project.name,
          printboxProjectUuid: project.uuid,
          priceDiscounted: project.params[0].price_net,
          price: project.params[0].price_gross,
          bookType: bookType?.id,
          bookSize: bookSize?.id,
          paperFinish: paperFinish?.id,
        }
      }));

      await strapi.db.query('api::ordered-project.ordered-project').delete({
        where: { project: {
          user: ctx.state.user.id 
        }}
      });

      if (projectsData.length > 0) {
        await strapi.db.query('api::project.project').createMany({ data: projectsData });
      }

      const cartedProjects = await strapi.db.query('api::project.project').findMany({
        where: { printboxProjectUuid: { $in: projectsPrintboxData.map(p => p.uuid) } },
      });

      const orderedProjectsData = projects.map((project) => ({
        quantity: project.quantity,
        project: {
          connect: cartedProjects.filter(p => p.printboxProjectUuid === project.projectId).map(p => p.id)[0]
        },
      }));

      const createdOrderedProjects = await Promise.all(orderedProjectsData.map(async (data) => {
        return await strapi.db.query('api::ordered-project.ordered-project').create({
          data
        });
      }));

      await cartService.appendOrderedProject(cart.id, createdOrderedProjects.map(p => p.id));
      
      ctx.send(await cartService.findOrCreate(ctx.state.user.id));
    } catch (error) {
      if (error instanceof ValidationError) {
        return ctx.badRequest('Validation error', error.errors);
      }

      strapi.log.error('Error updating user:', error);
      return ctx.internalServerError('An error occurred while updating the user');
    }
  }
}));