import { NextResponse } from "next/server";
import { z } from "zod";
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import {
  runCrudMutationGuardAfterSuccess,
  validateCrudMutationGuard
} from "@open-mercato/shared/lib/crud/mutation-guard";
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
import { createCustomersCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from "../openapi.js";
import { CustomerInteraction, CustomerTodoLink } from "../../data/entities.js";
import { todoLinkWithTodoCreateSchema } from "../../data/validators.js";
import { withOperationMetadata } from "../../lib/operationMetadata.js";
import { resolveCustomerInteractionFeatureFlags } from "../../lib/interactionFeatureFlags.js";
import { resolveCustomersRequestContext } from "../../lib/interactionRequestContext.js";
import {
  EXAMPLE_TODO_SOURCE,
  CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE
} from "../../lib/interactionCompatibility.js";
import {
  filterTodoRows,
  listCanonicalTodoRows,
  listLegacyTodoRows,
  normalizeTodoSearch,
  paginateTodoRows,
  sortTodoRows,
  resolveLegacyTodoDetails
} from "../../lib/todoCompatibility.js";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("customers");
const querySchema = z.object({
  page: z.coerce.number().min(1).default(1),
  pageSize: z.coerce.number().min(1).max(100).default(50),
  search: z.string().optional(),
  all: z.string().optional(),
  entityId: z.string().uuid().optional()
});
const todoCreateBodySchema = todoLinkWithTodoCreateSchema.omit({
  tenantId: true,
  organizationId: true
}).passthrough();
const todoUpdateBodySchema = z.object({
  id: z.string().uuid(),
  linkId: z.string().uuid().optional(),
  title: z.string().trim().min(1).max(200).optional(),
  isDone: z.boolean().optional(),
  is_done: z.boolean().optional(),
  customFields: z.record(z.string(), z.unknown()).optional(),
  customValues: z.record(z.string(), z.unknown()).optional()
}).passthrough();
const todoDeleteBodySchema = z.object({
  id: z.string().uuid(),
  todoId: z.string().uuid().optional()
});
const metadata = {
  GET: { requireAuth: true, requireFeatures: ["customers.view"] },
  POST: { requireAuth: true, requireFeatures: ["customers.interactions.manage"] },
  PUT: { requireAuth: true, requireFeatures: ["customers.interactions.manage"] },
  DELETE: { requireAuth: true, requireFeatures: ["customers.interactions.manage"] }
};
const DEPRECATION_HEADERS = {
  Deprecation: "true",
  Sunset: "Tue, 30 Jun 2026 00:00:00 GMT",
  Link: '</api/customers/interactions>; rel="successor-version"'
};
const MERGED_TODO_FETCH_CAP = 2e3;
function resolveGuardUserId(auth) {
  if (typeof auth.sub === "string" && auth.sub.trim().length > 0) return auth.sub;
  if (typeof auth.userId === "string" && auth.userId.trim().length > 0) return auth.userId;
  if (typeof auth.keyId === "string" && auth.keyId.trim().length > 0) return auth.keyId;
  return "system";
}
function withAdapterHeaders(response) {
  const headers = new Headers(response.headers);
  Object.entries(DEPRECATION_HEADERS).forEach(([key, value]) => headers.set(key, value));
  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers
  });
}
async function legacyAdaptersDisabledResponse() {
  const { translate } = await resolveTranslations();
  return withAdapterHeaders(NextResponse.json(
    {
      error: translate(
        "customers.interactions.legacyAdapters.disabled",
        "This legacy adapter has been disabled. Use /api/customers/interactions instead."
      )
    },
    { status: 410 }
  ));
}
function normalizeTodoStatusInput(body) {
  if (typeof body.isDone === "boolean") return body.isDone;
  if (typeof body.is_done === "boolean") return body.is_done;
  return void 0;
}
function collectTodoCustomValues(body) {
  const direct = body.customValues && typeof body.customValues === "object" ? { ...body.customValues } : {};
  if (body.todoCustom && typeof body.todoCustom === "object") {
    Object.assign(direct, body.todoCustom);
  }
  if (body.customFields && typeof body.customFields === "object") {
    Object.assign(direct, body.customFields);
  }
  return Object.keys(direct).length > 0 ? direct : void 0;
}
async function findLegacyTodoLink(em, target, tenantId) {
  if (target.linkId) {
    const byLinkId = await em.findOne(CustomerTodoLink, { id: target.linkId, tenantId }, { populate: ["entity"] });
    if (byLinkId) return byLinkId;
  }
  if (target.todoId) {
    return await em.findOne(CustomerTodoLink, { todoId: target.todoId, tenantId }, { populate: ["entity"] });
  }
  return null;
}
async function ensureCanonicalTodoBridge(em, queryEngine, commandBus, commandContext, link) {
  const existing = await em.findOne(CustomerInteraction, { id: link.todoId, tenantId: link.tenantId });
  if (existing) return existing.id;
  const detailMap = await resolveLegacyTodoDetails(
    queryEngine,
    [link],
    link.tenantId,
    [link.organizationId]
  );
  const source = typeof link.todoSource === "string" && link.todoSource.trim().length > 0 ? link.todoSource : EXAMPLE_TODO_SOURCE;
  const detail = detailMap.get(`${source}:${link.todoId}`) ?? null;
  const entityId = typeof link.entity === "string" ? link.entity : link.entity.id;
  await commandBus.execute("customers.interactions.create", {
    input: {
      id: link.todoId,
      tenantId: link.tenantId,
      organizationId: link.organizationId,
      entityId,
      interactionType: "task",
      title: detail?.title ?? null,
      body: detail?.description ?? null,
      status: detail?.isDone ? "done" : "planned",
      scheduledAt: detail?.dueAt ?? null,
      priority: detail?.priority ?? null,
      source: CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE,
      ...detail?.customValues ? { customValues: detail.customValues } : {}
    },
    ctx: commandContext
  });
  return link.todoId;
}
async function resolveCanonicalTodoTargetId(em, queryEngine, commandBus, commandContext, target, tenantId) {
  if (target.todoId) {
    const interaction = await em.findOne(CustomerInteraction, { id: target.todoId, tenantId });
    if (interaction) return interaction.id;
  }
  const legacyLink = await findLegacyTodoLink(em, target, tenantId);
  if (!legacyLink) {
    if (!target.todoId) throw new CrudHttpError(404, { error: "Todo not found" });
    return target.todoId;
  }
  return ensureCanonicalTodoBridge(
    em,
    queryEngine,
    commandBus,
    commandContext,
    legacyLink
  );
}
async function GET(request) {
  try {
    const { auth, em, organizationIds, container, selectedOrganizationId } = await resolveCustomersRequestContext(request);
    const query = querySchema.parse(Object.fromEntries(new URL(request.url).searchParams));
    const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId);
    if (!flags.legacyAdapters) {
      return await legacyAdaptersDisabledResponse();
    }
    const queryEngine = container.resolve("queryEngine");
    const exportAll = parseBooleanToken(query.all) === true;
    const search = normalizeTodoSearch(query.search);
    if (flags.unified) {
      const canonical = await listCanonicalTodoRows(
        em,
        container,
        auth,
        selectedOrganizationId,
        organizationIds,
        {
          entityId: query.entityId,
          pagination: exportAll ? null : { page: query.page, pageSize: query.pageSize },
          searchText: search
        }
      );
      const total = canonical.total;
      return withAdapterHeaders(
        NextResponse.json({
          items: canonical.items,
          total,
          page: exportAll ? 1 : query.page,
          pageSize: exportAll ? canonical.items.length : query.pageSize,
          totalPages: exportAll ? 1 : Math.max(1, Math.ceil(total / query.pageSize))
        })
      );
    }
    const legacyWindow = exportAll ? null : Math.min(MERGED_TODO_FETCH_CAP, Math.max(query.pageSize, query.page * query.pageSize + query.pageSize));
    const [legacyRows, canonicalRows] = await Promise.all([
      listLegacyTodoRows(em, queryEngine, auth.tenantId, organizationIds, query.entityId, {
        limit: legacyWindow
      }),
      listCanonicalTodoRows(
        em,
        container,
        auth,
        selectedOrganizationId,
        organizationIds,
        {
          entityId: query.entityId,
          includeDeleted: true,
          source: CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE,
          limit: legacyWindow
        }
      )
    ]);
    const mergedRows = [
      ...legacyRows.filter((row) => !canonicalRows.bridgeIds.has(row.todoId)),
      ...canonicalRows.items
    ];
    const filteredRows = filterTodoRows(sortTodoRows(mergedRows), search);
    const paged = paginateTodoRows(filteredRows, query.page, query.pageSize, exportAll);
    return withAdapterHeaders(
      NextResponse.json({
        items: paged.items,
        total: paged.total,
        page: paged.page,
        pageSize: paged.pageSize,
        totalPages: paged.totalPages
      })
    );
  } catch (err) {
    if (isCrudHttpError(err)) {
      return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }));
    }
    if (err instanceof z.ZodError) {
      return withAdapterHeaders(
        NextResponse.json({ error: "Validation failed", details: err.issues }, { status: 400 })
      );
    }
    logger.error("customers.todos.get failed", { err });
    return withAdapterHeaders(
      NextResponse.json({ error: "Internal server error" }, { status: 500 })
    );
  }
}
async function POST(request) {
  try {
    const { commandContext, container, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request);
    const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId);
    if (!flags.legacyAdapters) {
      return await legacyAdaptersDisabledResponse();
    }
    const commandBus = container.resolve("commandBus");
    const body = todoCreateBodySchema.parse(await readJsonSafe(request, {}));
    const guardUserId = resolveGuardUserId(auth);
    const guardResult = await validateCrudMutationGuard(container, {
      tenantId: auth.tenantId,
      organizationId: selectedOrganizationId,
      userId: guardUserId,
      resourceKind: "customers.todoLink",
      resourceId: body.entityId,
      operation: "create",
      requestMethod: request.method,
      requestHeaders: request.headers,
      mutationPayload: body
    });
    if (guardResult && !guardResult.ok) {
      return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }));
    }
    const customValues = collectTodoCustomValues(body);
    const { result, logEntry } = await commandBus.execute("customers.interactions.create", {
      input: {
        tenantId: auth.tenantId,
        organizationId: selectedOrganizationId ?? auth.orgId,
        entityId: body.entityId,
        interactionType: "task",
        title: body.title,
        status: body.is_done === true || body.isDone === true ? "done" : "planned",
        source: CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE,
        ...customValues ? {
          customValues,
          priority: typeof customValues.priority === "number" ? customValues.priority : null,
          body: typeof customValues.description === "string" ? customValues.description : null,
          scheduledAt: typeof customValues.due_at === "string" ? customValues.due_at : typeof customValues.dueAt === "string" ? customValues.dueAt : null
        } : {}
      },
      ctx: commandContext
    });
    if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
      await runCrudMutationGuardAfterSuccess(container, {
        tenantId: auth.tenantId,
        organizationId: selectedOrganizationId,
        userId: guardUserId,
        resourceKind: "customers.todoLink",
        resourceId: body.entityId,
        operation: "create",
        requestMethod: request.method,
        requestHeaders: request.headers,
        metadata: guardResult.metadata ?? null
      });
    }
    const interactionId = result && typeof result === "object" && "interactionId" in result && typeof result.interactionId === "string" ? result.interactionId : result && typeof result === "object" && "id" in result && typeof result.id === "string" ? result.id : null;
    return withAdapterHeaders(
      withOperationMetadata(
        NextResponse.json(
          {
            linkId: interactionId,
            todoId: interactionId
          },
          { status: 201 }
        ),
        logEntry,
        { resourceKind: "customers.todoLink", resourceId: interactionId }
      )
    );
  } catch (err) {
    if (isCrudHttpError(err)) {
      return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }));
    }
    if (err instanceof z.ZodError) {
      return withAdapterHeaders(
        NextResponse.json({ error: "Validation failed", details: err.issues }, { status: 400 })
      );
    }
    logger.error("customers.todos.post failed", { err });
    return withAdapterHeaders(
      NextResponse.json({ error: "Internal server error" }, { status: 500 })
    );
  }
}
async function PUT(request) {
  try {
    const { commandContext, container, em, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request);
    const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId);
    if (!flags.legacyAdapters) {
      return await legacyAdaptersDisabledResponse();
    }
    const commandBus = container.resolve("commandBus");
    const queryEngine = container.resolve("queryEngine");
    const body = todoUpdateBodySchema.parse(await readJsonSafe(request, {}));
    const guardUserId = resolveGuardUserId(auth);
    const guardResult = await validateCrudMutationGuard(container, {
      tenantId: auth.tenantId,
      organizationId: selectedOrganizationId,
      userId: guardUserId,
      resourceKind: "customers.todoLink",
      resourceId: body.linkId ?? body.id,
      operation: "update",
      requestMethod: request.method,
      requestHeaders: request.headers,
      mutationPayload: body
    });
    if (guardResult && !guardResult.ok) {
      return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }));
    }
    const interactionId = flags.unified ? body.id : await resolveCanonicalTodoTargetId(
      em,
      queryEngine,
      commandBus,
      commandContext,
      {
        todoId: body.id,
        linkId: body.linkId
      },
      auth.tenantId
    );
    const customValues = collectTodoCustomValues(body);
    const nextDone = normalizeTodoStatusInput(body);
    const { logEntry } = await commandBus.execute("customers.interactions.update", {
      input: {
        id: interactionId,
        ...body.title !== void 0 ? { title: body.title } : {},
        ...nextDone !== void 0 ? { status: nextDone ? "done" : "planned" } : {},
        ...customValues ? {
          customValues,
          priority: typeof customValues.priority === "number" ? customValues.priority : null,
          body: typeof customValues.description === "string" ? customValues.description : null,
          scheduledAt: typeof customValues.due_at === "string" ? customValues.due_at : typeof customValues.dueAt === "string" ? customValues.dueAt : null
        } : {}
      },
      ctx: commandContext
    });
    if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
      await runCrudMutationGuardAfterSuccess(container, {
        tenantId: auth.tenantId,
        organizationId: selectedOrganizationId,
        userId: guardUserId,
        resourceKind: "customers.todoLink",
        resourceId: body.linkId ?? body.id,
        operation: "update",
        requestMethod: request.method,
        requestHeaders: request.headers,
        metadata: guardResult.metadata ?? null
      });
    }
    return withAdapterHeaders(
      withOperationMetadata(
        NextResponse.json({ ok: true }),
        logEntry,
        { resourceKind: "customers.todoLink", resourceId: body.linkId ?? body.id }
      )
    );
  } catch (err) {
    if (isCrudHttpError(err)) {
      return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }));
    }
    if (err instanceof z.ZodError) {
      return withAdapterHeaders(
        NextResponse.json({ error: "Validation failed", details: err.issues }, { status: 400 })
      );
    }
    logger.error("customers.todos.put failed", { err });
    return withAdapterHeaders(
      NextResponse.json({ error: "Internal server error" }, { status: 500 })
    );
  }
}
async function DELETE(request) {
  try {
    const { commandContext, container, em, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request);
    const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId);
    if (!flags.legacyAdapters) {
      return await legacyAdaptersDisabledResponse();
    }
    const commandBus = container.resolve("commandBus");
    const queryEngine = container.resolve("queryEngine");
    const body = todoDeleteBodySchema.parse(await readJsonSafe(request, {}));
    const guardUserId = resolveGuardUserId(auth);
    const guardResult = await validateCrudMutationGuard(container, {
      tenantId: auth.tenantId,
      organizationId: selectedOrganizationId,
      userId: guardUserId,
      resourceKind: "customers.todoLink",
      resourceId: body.id,
      operation: "delete",
      requestMethod: request.method,
      requestHeaders: request.headers,
      mutationPayload: body
    });
    if (guardResult && !guardResult.ok) {
      return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }));
    }
    const interactionId = flags.unified ? body.todoId ?? body.id : await resolveCanonicalTodoTargetId(
      em,
      queryEngine,
      commandBus,
      commandContext,
      {
        linkId: body.id,
        todoId: body.todoId ?? body.id
      },
      auth.tenantId
    );
    const { logEntry } = await commandBus.execute("customers.interactions.delete", {
      input: { id: interactionId },
      ctx: commandContext
    });
    if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
      await runCrudMutationGuardAfterSuccess(container, {
        tenantId: auth.tenantId,
        organizationId: selectedOrganizationId,
        userId: guardUserId,
        resourceKind: "customers.todoLink",
        resourceId: body.id,
        operation: "delete",
        requestMethod: request.method,
        requestHeaders: request.headers,
        metadata: guardResult.metadata ?? null
      });
    }
    return withAdapterHeaders(
      withOperationMetadata(
        NextResponse.json({ ok: true }),
        logEntry,
        { resourceKind: "customers.todoLink", resourceId: body.id }
      )
    );
  } catch (err) {
    if (isCrudHttpError(err)) {
      return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }));
    }
    if (err instanceof z.ZodError) {
      return withAdapterHeaders(
        NextResponse.json({ error: "Validation failed", details: err.issues }, { status: 400 })
      );
    }
    logger.error("customers.todos.delete failed", { err });
    return withAdapterHeaders(
      NextResponse.json({ error: "Internal server error" }, { status: 500 })
    );
  }
}
const todoItemSchema = z.object({
  id: z.string(),
  todoId: z.string(),
  todoSource: z.string(),
  todoTitle: z.string().nullable(),
  todoIsDone: z.boolean().nullable(),
  todoPriority: z.number().nullable().optional(),
  todoSeverity: z.string().nullable().optional(),
  todoDescription: z.string().nullable().optional(),
  todoDueAt: z.string().nullable().optional(),
  todoCustomValues: z.record(z.string(), z.unknown()).nullable().optional(),
  todoOrganizationId: z.string().nullable(),
  todoUpdatedAt: z.string().nullable().optional(),
  organizationId: z.string(),
  tenantId: z.string(),
  createdAt: z.string(),
  externalHref: z.string().nullable().optional(),
  _integrations: z.record(z.string(), z.unknown()).optional(),
  customer: z.object({
    id: z.string().nullable(),
    displayName: z.string().nullable(),
    kind: z.string().nullable()
  })
});
const todoCreateResponseSchema = z.object({
  linkId: z.string().uuid().nullable(),
  todoId: z.string().uuid().nullable()
});
const openApi = createCustomersCrudOpenApi({
  resourceName: "CustomerTodo",
  querySchema,
  listResponseSchema: createPagedListResponseSchema(todoItemSchema),
  create: {
    schema: todoCreateBodySchema,
    responseSchema: todoCreateResponseSchema,
    description: "DEPRECATED (sunset 2026-06-30): Creates a customer task. Use POST /api/customers/interactions instead."
  },
  update: {
    schema: todoUpdateBodySchema,
    responseSchema: defaultOkResponseSchema,
    description: "DEPRECATED (sunset 2026-06-30): Updates a customer task. Use PUT /api/customers/interactions instead."
  },
  del: {
    schema: todoDeleteBodySchema,
    responseSchema: defaultOkResponseSchema,
    description: "DEPRECATED (sunset 2026-06-30): Deletes a customer task. Use DELETE /api/customers/interactions instead."
  }
});
export {
  DELETE,
  GET,
  POST,
  PUT,
  metadata,
  openApi
};
//# sourceMappingURL=route.js.map
