import type { RequestHandler } from "@sveltejs/kit";
import { json } from "@sveltejs/kit";
import { z } from "zod";
import { UniversalLogger } from "$lib/logger";
import { ValidationError, NotFoundError } from "$lib/server/utils/errors";
import { ClientPinResetService } from "$lib/server/services/client-pin-reset-service";
import { registerOpenAPIRoute } from "$lib/server/openapi";

const logger = new UniversalLogger().setContext("ClientPinResetCompleteAPI");

const completePinResetSchema = z.object({
  token: z.string().uuid(),
  newClientPublicKey: z.string().min(1),
  newPrivateKeyShare: z.string().min(1),
  newClientEncryptedTunnelKey: z.string().min(1),
});

registerOpenAPIRoute("/tenants/{id}/clients/pin-reset/complete", "POST", {
  summary: "Complete PIN reset process",
  description:
    "Completes the PIN reset process by verifying the token and updating the client's cryptographic keys. This endpoint receives the new keypair generated by the client and the re-encrypted tunnel key. It updates the client tunnel with the new keys and marks the reset token as used. Staff key shares remain unchanged as the tunnel key itself doesn't change. This endpoint is public (no authentication required) as the client uses it directly.",
  tags: ["Clients", "PIN Reset"],
  parameters: [
    {
      name: "id",
      in: "path",
      required: true,
      schema: { type: "string", format: "uuid" },
      description: "Tenant ID",
    },
  ],
  requestBody: {
    description: "New cryptographic keys after PIN reset",
    content: {
      "application/json": {
        schema: {
          type: "object",
          properties: {
            token: {
              type: "string",
              format: "uuid",
              description: "Reset token received via QR code or email",
            },
            newClientPublicKey: {
              type: "string",
              description: "Base64 encoded new ML-KEM-768 public key",
            },
            newPrivateKeyShare: {
              type: "string",
              description: "Base64 encoded new private key share (derived from new PIN)",
            },
            newClientEncryptedTunnelKey: {
              type: "string",
              description: "Hex encoded tunnel key encrypted with new client public key",
            },
          },
          required: [
            "token",
            "newClientPublicKey",
            "newPrivateKeyShare",
            "newClientEncryptedTunnelKey",
          ],
        },
      },
    },
  },
  responses: {
    "200": {
      description: "PIN reset completed successfully",
      content: {
        "application/json": {
          schema: {
            type: "object",
            properties: {
              success: {
                type: "boolean",
                description: "Success status",
              },
              tunnelId: {
                type: "string",
                format: "uuid",
                description: "Client tunnel ID",
              },
              message: {
                type: "string",
                description: "Success message",
              },
            },
            required: ["success", "tunnelId", "message"],
          },
        },
      },
    },
    "400": {
      description: "Invalid input data or token expired/used",
      content: {
        "application/json": {
          schema: { $ref: "#/components/schemas/Error" },
        },
      },
    },
    "404": {
      description: "Token or client not found",
      content: {
        "application/json": {
          schema: { $ref: "#/components/schemas/Error" },
        },
      },
    },
    "500": {
      description: "Internal server error",
      content: {
        "application/json": {
          schema: { $ref: "#/components/schemas/Error" },
        },
      },
    },
  },
});

/**
 * POST /api/tenants/[id]/clients/pin-reset/complete
 *
 * Complete the PIN reset process
 * - Verifies the reset token (validity, expiration, usage)
 * - Updates client's keypair
 * - Re-encrypts tunnel key with new client keypair
 * - Marks token as used
 *
 * Note: Staff key shares remain unchanged as the tunnel key itself doesn't change.
 *
 * This endpoint is PUBLIC (no authentication) because it's called by the client
 * who doesn't have valid credentials yet (that's why they're resetting the PIN)
 */
export const POST: RequestHandler = async ({ params, request }) => {
  const tenantId = params.id!;

  try {
    logger.debug("PIN reset complete request received", { tenantId });

    // Parse and validate request body
    const body = await request.json();
    const validatedData = completePinResetSchema.parse(body);

    logger.debug("Request validated", {
      tokenId: validatedData.token.slice(0, 8),
    });

    // Create PIN reset service
    const pinResetService = await ClientPinResetService.forTenant(tenantId);

    // Complete the PIN reset (verifies token, updates keys, marks token as used)
    const tunnelId = await pinResetService.completePinReset(
      validatedData.token,
      validatedData.newClientPublicKey,
      validatedData.newPrivateKeyShare,
      validatedData.newClientEncryptedTunnelKey,
    );

    logger.info("PIN reset completed successfully", {
      tenantId,
      tunnelId,
      tokenId: validatedData.token.slice(0, 8),
    });

    return json({
      success: true,
      tunnelId,
      message: "PIN reset completed successfully",
    });
  } catch (error) {
    if (error instanceof z.ZodError) {
      logger.warn("Validation error in PIN reset complete", { error: error });
      return json({ error: "Invalid request data", details: error }, { status: 400 });
    }

    if (error instanceof NotFoundError) {
      logger.warn("Token or client not found in PIN reset complete", { tenantId });
      return json({ error: error.message }, { status: 404 });
    }

    if (error instanceof ValidationError) {
      logger.warn("Validation error in PIN reset complete", { error: error.message });
      return json({ error: error.message }, { status: 400 });
    }

    logger.error("Unexpected error in PIN reset complete", { error: String(error), tenantId });
    return json({ error: "Internal server error" }, { status: 500 });
  }
};
