import { registerCommand } from "@open-mercato/shared/lib/commands";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { PlannerAvailabilityRule } from "../data/entities.js";
import {
  plannerAvailabilityRuleCreateSchema,
  plannerAvailabilityRuleUpdateSchema
} from "../data/validators.js";
import { ensureOrganizationScope, ensureTenantScope } from "./shared.js";
import { extractUndoPayload } from "./shared.js";
import { resolveRedoSnapshot } from "@open-mercato/shared/lib/commands/redo";
const AVAILABILITY_RULE_RESOURCE_KIND = "planner.availability.rule";
async function loadAvailabilityRuleSnapshot(em, id) {
  const record = await em.findOne(PlannerAvailabilityRule, { id });
  if (!record) return null;
  return {
    id: record.id,
    tenantId: record.tenantId,
    organizationId: record.organizationId,
    subjectType: record.subjectType,
    subjectId: record.subjectId,
    timezone: record.timezone,
    rrule: record.rrule,
    exdates: [...record.exdates ?? []],
    kind: record.kind,
    note: record.note ?? null,
    unavailabilityReasonEntryId: record.unavailabilityReasonEntryId ?? null,
    unavailabilityReasonValue: record.unavailabilityReasonValue ?? null,
    deletedAt: record.deletedAt ?? null
  };
}
async function restoreAvailabilityRuleFromSnapshot(em, snapshot) {
  let record = await em.findOne(PlannerAvailabilityRule, { id: snapshot.id });
  if (!record) {
    record = em.create(PlannerAvailabilityRule, {
      id: snapshot.id,
      tenantId: snapshot.tenantId,
      organizationId: snapshot.organizationId,
      subjectType: snapshot.subjectType,
      subjectId: snapshot.subjectId,
      timezone: snapshot.timezone,
      rrule: snapshot.rrule,
      exdates: snapshot.exdates ?? [],
      kind: snapshot.kind ?? "availability",
      note: snapshot.note ?? null,
      unavailabilityReasonEntryId: snapshot.unavailabilityReasonEntryId ?? null,
      unavailabilityReasonValue: snapshot.unavailabilityReasonValue ?? null,
      createdAt: /* @__PURE__ */ new Date(),
      updatedAt: /* @__PURE__ */ new Date()
    });
    em.persist(record);
  } else {
    record.subjectType = snapshot.subjectType;
    record.subjectId = snapshot.subjectId;
    record.timezone = snapshot.timezone;
    record.rrule = snapshot.rrule;
    record.exdates = snapshot.exdates ?? [];
    record.kind = snapshot.kind ?? "availability";
    record.note = snapshot.note ?? null;
    record.unavailabilityReasonEntryId = snapshot.unavailabilityReasonEntryId ?? null;
    record.unavailabilityReasonValue = snapshot.unavailabilityReasonValue ?? null;
    record.deletedAt = snapshot.deletedAt ?? null;
  }
}
const createAvailabilityRuleCommand = {
  id: "planner.availability.create",
  async execute(input, ctx) {
    const parsed = plannerAvailabilityRuleCreateSchema.parse(input);
    ensureTenantScope(ctx, parsed.tenantId);
    ensureOrganizationScope(ctx, parsed.organizationId);
    const em = ctx.container.resolve("em").fork();
    const now = /* @__PURE__ */ new Date();
    const kind = parsed.kind ?? "availability";
    const unavailabilityReasonEntryId = kind === "unavailability" ? parsed.unavailabilityReasonEntryId ?? null : null;
    const unavailabilityReasonValue = kind === "unavailability" ? parsed.unavailabilityReasonValue ?? null : null;
    const record = em.create(PlannerAvailabilityRule, {
      tenantId: parsed.tenantId,
      organizationId: parsed.organizationId,
      subjectType: parsed.subjectType,
      subjectId: parsed.subjectId,
      timezone: parsed.timezone,
      rrule: parsed.rrule,
      exdates: parsed.exdates ?? [],
      kind,
      note: parsed.note ?? null,
      unavailabilityReasonEntryId,
      unavailabilityReasonValue,
      createdAt: now,
      updatedAt: now
    });
    em.persist(record);
    await em.flush();
    return { ruleId: record.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    return loadAvailabilityRuleSnapshot(em, result.ruleId);
  },
  buildLog: async ({ input, result, ctx, snapshots }) => {
    const snapshot = snapshots.after;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("planner.audit.availability.create", "Create availability rule"),
      resourceKind: AVAILABILITY_RULE_RESOURCE_KIND,
      resourceId: result?.ruleId ?? null,
      tenantId: input?.tenantId ?? ctx.auth?.tenantId ?? null,
      organizationId: input?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,
      snapshotAfter: snapshot ?? null,
      payload: {
        undo: {
          after: snapshot
        }
      }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const ruleId = logEntry?.resourceId ?? null;
    if (!ruleId) return;
    const em = ctx.container.resolve("em").fork();
    const record = await em.findOne(PlannerAvailabilityRule, { id: ruleId });
    if (record) {
      record.deletedAt = /* @__PURE__ */ new Date();
      await em.flush();
    }
  },
  redo: async ({ logEntry, ctx }) => {
    const after = resolveRedoSnapshot(logEntry);
    if (!after) throw new CrudHttpError(400, { error: "[internal] redo snapshot unavailable for availability rule create" });
    const em = ctx.container.resolve("em").fork();
    await restoreAvailabilityRuleFromSnapshot(em, { ...after, deletedAt: null });
    await em.flush();
    return { ruleId: after.id };
  }
};
const updateAvailabilityRuleCommand = {
  id: "planner.availability.update",
  async prepare(input, ctx) {
    const parsed = plannerAvailabilityRuleUpdateSchema.parse(input);
    const em = ctx.container.resolve("em");
    const snapshot = await loadAvailabilityRuleSnapshot(em, parsed.id);
    return snapshot ? { before: snapshot } : {};
  },
  async execute(input, ctx) {
    const parsed = plannerAvailabilityRuleUpdateSchema.parse(input);
    const em = ctx.container.resolve("em").fork();
    const record = await em.findOne(PlannerAvailabilityRule, { id: parsed.id, deletedAt: null });
    if (!record) throw new CrudHttpError(404, { error: "Planner availability rule not found." });
    ensureTenantScope(ctx, record.tenantId);
    ensureOrganizationScope(ctx, record.organizationId);
    if (parsed.subjectType !== void 0) record.subjectType = parsed.subjectType;
    if (parsed.subjectId !== void 0) record.subjectId = parsed.subjectId;
    if (parsed.timezone !== void 0) record.timezone = parsed.timezone;
    if (parsed.rrule !== void 0) record.rrule = parsed.rrule;
    if (parsed.exdates !== void 0) record.exdates = parsed.exdates;
    if (parsed.kind !== void 0) record.kind = parsed.kind;
    if (parsed.note !== void 0) record.note = parsed.note ?? null;
    const nextKind = parsed.kind ?? record.kind;
    if (nextKind !== "unavailability") {
      record.unavailabilityReasonEntryId = null;
      record.unavailabilityReasonValue = null;
    } else {
      if (parsed.unavailabilityReasonEntryId !== void 0) {
        record.unavailabilityReasonEntryId = parsed.unavailabilityReasonEntryId ?? null;
      }
      if (parsed.unavailabilityReasonValue !== void 0) {
        record.unavailabilityReasonValue = parsed.unavailabilityReasonValue ?? null;
      }
    }
    await em.flush();
    return { ruleId: record.id };
  },
  buildLog: async ({ snapshots, input, result, ctx }) => {
    const before = snapshots.before;
    const em = ctx.container.resolve("em").fork();
    const afterSnapshot = before ? await loadAvailabilityRuleSnapshot(em, before.id) : null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("planner.audit.availability.update", "Update availability rule"),
      resourceKind: AVAILABILITY_RULE_RESOURCE_KIND,
      resourceId: result?.ruleId ?? input?.id ?? null,
      tenantId: ctx.auth?.tenantId ?? null,
      organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,
      snapshotBefore: before ?? null,
      snapshotAfter: afterSnapshot ?? null,
      payload: {
        undo: {
          before: before ?? null,
          after: afterSnapshot ?? null
        }
      }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const before = payload?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    await restoreAvailabilityRuleFromSnapshot(em, before);
    await em.flush();
  }
};
const deleteAvailabilityRuleCommand = {
  id: "planner.availability.delete",
  async prepare(input, ctx) {
    const id = input?.id;
    if (!id) return {};
    const em = ctx.container.resolve("em");
    const snapshot = await loadAvailabilityRuleSnapshot(em, id);
    return snapshot ? { before: snapshot } : {};
  },
  async execute(input, ctx) {
    const id = input?.id;
    if (!id) throw new CrudHttpError(400, { error: "Availability rule id is required." });
    const em = ctx.container.resolve("em").fork();
    const record = await em.findOne(PlannerAvailabilityRule, { id, deletedAt: null });
    if (!record) return { ruleId: id };
    ensureTenantScope(ctx, record.tenantId);
    ensureOrganizationScope(ctx, record.organizationId);
    record.deletedAt = /* @__PURE__ */ new Date();
    await em.flush();
    return { ruleId: record.id };
  },
  buildLog: async ({ snapshots, input, result, ctx }) => {
    const before = snapshots.before;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("planner.audit.availability.delete", "Delete availability rule"),
      resourceKind: AVAILABILITY_RULE_RESOURCE_KIND,
      resourceId: result?.ruleId ?? input?.id ?? null,
      tenantId: ctx.auth?.tenantId ?? null,
      organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,
      snapshotBefore: before ?? null,
      payload: {
        undo: {
          before: before ?? null
        }
      }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const before = payload?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    await restoreAvailabilityRuleFromSnapshot(em, { ...before, deletedAt: null });
    await em.flush();
  }
};
registerCommand(createAvailabilityRuleCommand);
registerCommand(updateAvailabilityRuleCommand);
registerCommand(deleteAvailabilityRuleCommand);
//# sourceMappingURL=availability.js.map
