/**
 * Interview Token Validation API
 *
 * GET /api/careers/interview/validate-token?token=[TOKEN]
 *
 * Validates interview scheduling token and returns job/applicant details.
 * Public endpoint (no auth required) - access controlled by token.
 *
 * Security:
 * - Rate limiting (10 requests per minute per IP)
 * - Token validation (expiry, usage, revocation)
 * - SQL injection prevention (Prisma parameterized queries)
 *
 * @module app/api/careers/interview/validate-token
 */

import { NextRequest, NextResponse } from "next/server";
import { withRateLimit } from "@/lib/rate-limiter";
import { interviewTokenService } from "@/lib/services/interview-tokens.service";
import { prisma } from "@/lib/db";
import { logger } from "@/lib/logger";
import { ApiResponse } from "@/lib/api/response-helpers";
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
import { getDataSource } from "@/lib/data-source";

export const dynamic = "force-dynamic";
export const runtime = "nodejs";

/**
 * Create rate limiter for token validation
 * 10 requests per minute per IP
 */
const redis =
  process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN
    ? new Redis({
        url: process.env.UPSTASH_REDIS_REST_URL,
        token: process.env.UPSTASH_REDIS_REST_TOKEN,
      })
    : null;

const tokenValidationLimiter = redis
  ? new Ratelimit({
      redis,
      limiter: Ratelimit.slidingWindow(10, "1 m"),
      analytics: true,
      prefix: "ratelimit:interview_token_validation",
    })
  : {
      limit: async () => ({
        success: true,
        limit: 10,
        reset: Date.now() + 60000,
        remaining: 9,
      }),
    };

/**
 * Validate interview token handler
 */
async function validateTokenHandler(request: NextRequest) {
  try {
    // Extract token from query params
    const { searchParams } = new URL(request.url);
    const token = searchParams.get("token");

    if (!token) {
      return ApiResponse.badRequest("Token parameter is required");
    }

    // ── Convex data-source branch ──────────────────────────────────────
    if (getDataSource("careers") === "convex") {
      try {
        const { convexQuery } = await import("@/lib/convex-client");

        const result = (await convexQuery(
          "careers/queries:validateInterviewToken",
          {
            token,
          },
        )) as {
          valid: boolean;
          errorCode?: string;
          error?: string;
          tokenRecord?: {
            id: string;
            expiresAt: number;
            usedAt: unknown;
            isRevoked: boolean;
          };
          payload?: { applicationId: string; jobId: string | null };
          application?: {
            id: string;
            applicantName: string;
            applicantEmail: string;
            applicantPhone: string;
            status: string;
          } | null;
          job?: {
            id: string;
            title: string;
            titleAr: string;
            department: string;
            departmentAr: string;
            location: string;
            locationAr: string;
            employmentType: string;
            description: string;
            descriptionAr: string;
          } | null;
        } | null;

        if (result && !result.valid) {
          switch (result.errorCode) {
            case "EXPIRED":
              return NextResponse.json(
                { error: result.error || "Token has expired" },
                { status: 410 },
              );
            case "USED":
              return ApiResponse.conflict(
                result.error || "Token has already been used",
              );
            case "REVOKED":
              return ApiResponse.forbidden(
                result.error || "Token has been revoked",
              );
            default:
              return ApiResponse.badRequest(result.error || "Invalid token");
          }
        }

        if (
          result?.valid &&
          result.tokenRecord &&
          result.application &&
          result.job
        ) {
          const now = new Date();
          const expiresAt = new Date(result.tokenRecord.expiresAt);
          const hoursRemaining = Math.max(
            0,
            Math.floor(
              (expiresAt.getTime() - now.getTime()) / (1000 * 60 * 60),
            ),
          );

          logger.info("[ValidateToken][Convex] Token validated successfully", {
            tokenId: result.tokenRecord.id,
          });

          return ApiResponse.ok({
            valid: true,
            token: {
              id: result.tokenRecord.id,
              expiresAt: result.tokenRecord.expiresAt,
              hoursRemaining,
              isExpired: now > expiresAt,
            },
            job: {
              id: result.job.id,
              title: result.job.title,
              titleAr: result.job.titleAr,
              department: result.job.department,
              departmentAr: result.job.departmentAr,
              location: result.job.location,
              locationAr: result.job.locationAr,
              employmentType: result.job.employmentType,
              description: result.job.description,
              descriptionAr: result.job.descriptionAr,
            },
            applicant: {
              name: result.application.applicantName,
              email: result.application.applicantEmail,
              phone: result.application.applicantPhone,
              positionApplied: "",
            },
            application: {
              id: result.application.id,
              status: result.application.status,
            },
          });
        }
      } catch (error) {
        console.error(
          "[Convex fallback] careers/interview/validate-token:",
          error,
        );
        // Fall through to Prisma
      }
    }

    // Validate token
    const validationResult =
      await interviewTokenService.validateInterviewToken(token);

    if (!validationResult.valid) {
      // Map error codes to appropriate HTTP responses
      switch (validationResult.errorCode) {
        case "EXPIRED":
          return ApiResponse.gone(
            validationResult.error || "Token has expired",
          );
        case "USED":
          return ApiResponse.conflict(
            validationResult.error || "Token has already been used",
          );
        case "REVOKED":
          return ApiResponse.forbidden(
            validationResult.error || "Token has been revoked",
          );
        case "INVALID":
        case "NOT_FOUND":
        default:
          return ApiResponse.badRequest(
            validationResult.error || "Invalid token",
          );
      }
    }

    const { payload, tokenRecord } = validationResult;

    if (!payload || !tokenRecord) {
      return ApiResponse.badRequest("Invalid token data");
    }

    // Fetch job and application details
    const [application, job] = await Promise.all([
      prisma.job_applications.findUnique({
        where: { id: payload.applicationId },
        select: {
          id: true,
          applicantName: true,
          applicantEmail: true,
          applicantPhone: true,
          positionApplied: true,
          status: true,
        },
      }),
      prisma.jobs.findUnique({
        where: { id: payload.jobId },
        select: {
          id: true,
          title: true,
          titleAr: true,
          department: true,
          departmentAr: true,
          location: true,
          locationAr: true,
          employmentType: true,
          description: true,
          descriptionAr: true,
        },
      }),
    ]);

    if (!application || !job) {
      logger.error("[ValidateToken] Application or job not found", {
        applicationId: payload.applicationId,
        jobId: payload.jobId,
      });
      return ApiResponse.notFound("Application or job not found");
    }

    // Check if token already used
    if (tokenRecord.usedAt) {
      return ApiResponse.conflict(
        "This token has already been used to book an interview",
      );
    }

    // Check if token revoked
    if (tokenRecord.isRevoked) {
      return ApiResponse.forbidden("This invitation has been revoked");
    }

    // Calculate time remaining
    const now = new Date();
    const expiresAt = new Date(tokenRecord.expiresAt);
    const hoursRemaining = Math.max(
      0,
      Math.floor((expiresAt.getTime() - now.getTime()) / (1000 * 60 * 60)),
    );

    logger.info("[ValidateToken] Token validated successfully", {
      tokenId: tokenRecord.id,
      applicationId: application.id,
      jobId: job.id,
    });

    // Return token details with job and applicant info
    return ApiResponse.ok({
      valid: true,
      token: {
        id: tokenRecord.id,
        expiresAt: tokenRecord.expiresAt,
        hoursRemaining,
        isExpired: now > expiresAt,
      },
      job: {
        id: job.id,
        title: job.title,
        titleAr: job.titleAr,
        department: job.department,
        departmentAr: job.departmentAr,
        location: job.location,
        locationAr: job.locationAr,
        employmentType: job.employmentType,
        description: job.description,
        descriptionAr: job.descriptionAr,
      },
      applicant: {
        name: application.applicantName,
        email: application.applicantEmail,
        phone: application.applicantPhone,
        positionApplied: application.positionApplied,
      },
      application: {
        id: application.id,
        status: application.status,
      },
    });
  } catch (error) {
    logger.error("[ValidateToken] Validation error:", error);
    return ApiResponse.serverError("Failed to validate token");
  }
}

// Apply rate limiting
export const GET = withRateLimit(validateTokenHandler, tokenValidationLimiter);
