import { NextResponse } from "next/server";
import { z } from "zod";
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { hashAuthToken } from "../../../../../auth/lib/tokenHash.js";
import {
  SalesQuote,
  SalesQuoteLine,
  SalesQuoteAdjustment
} from "../../../../data/entities.js";
import { canonicalizeUnitCode } from "@open-mercato/shared/lib/units/unitCodes";
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("sales");
const paramsSchema = z.object({
  token: z.string().uuid()
});
const metadata = {
  GET: { requireAuth: false }
};
async function GET(req, ctx) {
  try {
    const { token } = paramsSchema.parse(ctx.params ?? {});
    const container = await createRequestContainer();
    const em = container.resolve("em");
    const hashedToken = hashAuthToken(token);
    const quote = await findOneWithDecryption(em, SalesQuote, {
      acceptanceToken: hashedToken,
      deletedAt: null
    }) ?? await findOneWithDecryption(em, SalesQuote, {
      acceptanceToken: token,
      deletedAt: null
    });
    const { translate } = await resolveTranslations();
    if (!quote) {
      throw new CrudHttpError(404, {
        error: translate("sales.quotes.public.notFound", "Quote not found.")
      });
    }
    const auth = await getAuthFromRequest(req);
    if (auth?.tenantId && quote.tenantId !== auth.tenantId) {
      throw new CrudHttpError(404, {
        error: translate("sales.quotes.public.notFound", "Quote not found.")
      });
    }
    const now = /* @__PURE__ */ new Date();
    const isExpired = !!quote.validUntil && quote.validUntil.getTime() < now.getTime();
    const [lines, adjustments] = await Promise.all([
      findWithDecryption(
        em,
        SalesQuoteLine,
        { quote: quote.id, organizationId: quote.organizationId, tenantId: quote.tenantId, deletedAt: null },
        { orderBy: { lineNumber: "asc" } }
      ),
      findWithDecryption(
        em,
        SalesQuoteAdjustment,
        { quote: quote.id, organizationId: quote.organizationId, tenantId: quote.tenantId },
        { orderBy: { position: "asc" } }
      )
    ]);
    return NextResponse.json({
      quote: {
        quoteNumber: quote.quoteNumber,
        currencyCode: quote.currencyCode,
        validFrom: quote.validFrom?.toISOString() ?? null,
        validUntil: quote.validUntil?.toISOString() ?? null,
        status: quote.status ?? null,
        subtotalNetAmount: quote.subtotalNetAmount,
        subtotalGrossAmount: quote.subtotalGrossAmount,
        discountTotalAmount: quote.discountTotalAmount,
        taxTotalAmount: quote.taxTotalAmount,
        grandTotalNetAmount: quote.grandTotalNetAmount,
        grandTotalGrossAmount: quote.grandTotalGrossAmount
      },
      lines: lines.map((line) => ({
        lineNumber: line.lineNumber ?? null,
        kind: line.kind,
        name: line.name ?? null,
        description: line.description ?? null,
        quantity: line.quantity,
        quantityUnit: canonicalizeUnitCode(line.quantityUnit) ?? null,
        normalizedQuantity: line.normalizedQuantity ?? line.quantity,
        normalizedUnit: canonicalizeUnitCode(line.normalizedUnit ?? line.quantityUnit) ?? null,
        uomSnapshot: line.uomSnapshot ? {
          baseUnitCode: line.uomSnapshot.baseUnitCode ?? null,
          enteredUnitCode: line.uomSnapshot.enteredUnitCode ?? null
        } : null,
        currencyCode: line.currencyCode,
        unitPriceNet: line.unitPriceNet,
        unitPriceGross: line.unitPriceGross,
        discountAmount: line.discountAmount,
        discountPercent: line.discountPercent,
        taxRate: line.taxRate,
        taxAmount: line.taxAmount,
        totalNetAmount: line.totalNetAmount,
        totalGrossAmount: line.totalGrossAmount,
        unitPriceReference: (() => {
          if (!line.uomSnapshot) return null;
          const ref = line.uomSnapshot.unitPriceReference;
          if (!ref) return null;
          return {
            enabled: ref.enabled ?? null,
            referenceUnitCode: ref.referenceUnitCode ?? null,
            baseQuantity: ref.baseQuantity ?? null,
            grossPerReference: ref.grossPerReference ?? null,
            netPerReference: ref.netPerReference ?? null
          };
        })()
      })),
      adjustments: adjustments.map((adj) => ({
        scope: adj.scope,
        kind: adj.kind,
        label: adj.label ?? adj.code ?? null,
        rate: adj.rate,
        amountNet: adj.amountNet,
        amountGross: adj.amountGross,
        currencyCode: adj.currencyCode ?? null,
        position: adj.position ?? null,
        quoteLineId: adj.quoteLine?.id ?? null
      })),
      isExpired
    });
  } catch (err) {
    if (isCrudHttpError(err)) {
      return NextResponse.json(err.body, { status: err.status });
    }
    const { translate } = await resolveTranslations();
    logger.error("sales.quotes.public failed", { err });
    return NextResponse.json(
      {
        error: translate("sales.quotes.public.failed", "Failed to load quote.")
      },
      { status: 400 }
    );
  }
}
const publicQuoteResponseSchema = z.object({
  quote: z.object({
    quoteNumber: z.string(),
    currencyCode: z.string(),
    validFrom: z.string().nullable(),
    validUntil: z.string().nullable(),
    status: z.string().nullable(),
    subtotalNetAmount: z.string(),
    subtotalGrossAmount: z.string(),
    discountTotalAmount: z.string(),
    taxTotalAmount: z.string(),
    grandTotalNetAmount: z.string(),
    grandTotalGrossAmount: z.string()
  }),
  lines: z.array(
    z.object({
      lineNumber: z.number().nullable(),
      kind: z.string(),
      name: z.string().nullable(),
      description: z.string().nullable(),
      quantity: z.string(),
      quantityUnit: z.string().nullable(),
      normalizedQuantity: z.string(),
      normalizedUnit: z.string().nullable(),
      uomSnapshot: z.object({
        baseUnitCode: z.string().nullable(),
        enteredUnitCode: z.string().nullable()
      }).nullable().optional(),
      currencyCode: z.string(),
      unitPriceNet: z.string(),
      unitPriceGross: z.string(),
      discountAmount: z.string(),
      discountPercent: z.string(),
      taxRate: z.string(),
      taxAmount: z.string(),
      totalNetAmount: z.string(),
      totalGrossAmount: z.string(),
      unitPriceReference: z.object({
        enabled: z.boolean().nullable().optional(),
        referenceUnitCode: z.string().nullable().optional(),
        baseQuantity: z.string().nullable().optional(),
        grossPerReference: z.string().nullable().optional(),
        netPerReference: z.string().nullable().optional()
      }).nullable().optional()
    })
  ),
  adjustments: z.array(
    z.object({
      scope: z.string().nullable(),
      kind: z.string().nullable(),
      label: z.string().nullable(),
      rate: z.string().nullable(),
      amountNet: z.string().nullable(),
      amountGross: z.string().nullable(),
      currencyCode: z.string().nullable(),
      position: z.number().nullable(),
      quoteLineId: z.string().uuid().nullable()
    })
  ),
  isExpired: z.boolean()
});
const openApi = {
  tag: "Sales",
  summary: "View a quote (public)",
  pathParams: z.object({ token: z.string().uuid() }),
  methods: {
    GET: {
      summary: "Get quote details by acceptance token",
      responses: [
        {
          status: 200,
          description: "Quote details",
          schema: publicQuoteResponseSchema
        },
        {
          status: 404,
          description: "Quote not found",
          schema: z.object({ error: z.string() })
        }
      ]
    }
  }
};
export {
  GET,
  metadata,
  openApi
};
//# sourceMappingURL=route.js.map
