import { createContext } from "@tailor-platform/erp-kit/app";
import { createResolver, t } from "@tailor-platform/sdk";
import { getDB } from "@/generated/kysely-tailordb";
import { pmModules } from "@/modules";

export default createResolver({
  name: "updateProductAttributes",
  operation: "mutation",
  input: {
    productId: t.uuid().description("Product ID"),
    attributes: t
      .object(
        {
          attributeId: t.uuid().description("Attribute ID"),
          valueId: t.uuid().description("Value ID"),
        },
        { array: true },
      )
      .description("Desired attribute value assignments"),
  },
  body: async (context) => {
    const db = getDB("main-db");
    const result = await db.transaction().execute(async (trx) => {
      const ctx = createContext(context);

      const currentAssignments = await trx
        .selectFrom("ProductAttributeAssignment")
        .select(["attributeId", "valueId"])
        .where("productId", "=", context.input.productId)
        .execute();

      const currentSet = new Set(currentAssignments.map((a) => `${a.attributeId}:${a.valueId}`));
      const desiredSet = new Set(
        context.input.attributes.map((a) => `${a.attributeId}:${a.valueId}`),
      );

      const toAssign = context.input.attributes.filter(
        (a) => !currentSet.has(`${a.attributeId}:${a.valueId}`),
      );
      const toRemove = currentAssignments.filter(
        (a) => !desiredSet.has(`${a.attributeId}:${a.valueId}`),
      );

      for (const attr of toAssign) {
        const r = await pmModules.commands.assignAttributeValueToProduct(
          trx,
          {
            productId: context.input.productId,
            attributeId: attr.attributeId,
            valueId: attr.valueId,
          },
          ctx,
        );
        if (!r.ok) {
          switch (r.error.code) {
            case "PRODUCT_MANAGEMENT_PRODUCT_NOT_FOUND":
              throw new Error(`Product ${context.input.productId} does not exist`);
            case "PRODUCT_MANAGEMENT_ATTRIBUTE_NOT_FOUND":
              throw new Error(`Attribute ${attr.attributeId} does not exist`);
            case "PRODUCT_MANAGEMENT_PRODUCT_ARCHIVED":
              throw new Error(`Product is archived`);
            case "PRODUCT_MANAGEMENT_AXIS_STRUCTURE_LOCKED":
              throw new Error("Axis structure is locked");
            case "PRODUCT_MANAGEMENT_INVALID_VALUE":
              throw new Error(`Invalid attribute value: ${r.error.message}`);
            case "INSUFFICIENT_PERMISSION":
              throw r.error;
            default:
              throw r.error satisfies never;
          }
        }
      }

      for (const attr of toRemove) {
        const r = await pmModules.commands.removeAttributeValueFromProduct(
          trx,
          {
            productId: context.input.productId,
            attributeId: attr.attributeId,
            valueId: attr.valueId,
          },
          ctx,
        );
        if (!r.ok) {
          switch (r.error.code) {
            case "PRODUCT_MANAGEMENT_PRODUCT_NOT_FOUND":
              throw new Error(`Product ${context.input.productId} does not exist`);
            case "PRODUCT_MANAGEMENT_ASSIGNMENT_NOT_FOUND":
              break;
            case "PRODUCT_MANAGEMENT_PRODUCT_ARCHIVED":
              throw new Error(`Product is archived`);
            case "PRODUCT_MANAGEMENT_AXIS_STRUCTURE_LOCKED":
              throw new Error("Axis structure is locked");
            case "INSUFFICIENT_PERMISSION":
              throw r.error;
            default:
              throw r.error satisfies never;
          }
        }
      }

      return { productId: context.input.productId };
    });

    return { productId: result.productId };
  },
  output: t
    .object({
      productId: t.uuid().description("Product ID"),
    })
    .description("Product attributes update result"),
});
