import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import DOMPurify from "isomorphic-dompurify";
import { FILE_UPLOAD_CONFIG } from "@/lib/constants/storage.constants";
import {
  sendContactThankYouEmail,
  sendStaffContactNotification,
} from "@/lib/email";
import { getErrorMessage } from "@/lib/errors";
import { logger } from "@/lib/logger";
import { getClientIp } from "@/lib/utils/ip-extraction";
import { withSecureRoute } from "@/lib/api-security";
import { rateLimiters } from "@/lib/rate-limiter";
import {
  detectSuspiciousContent,
  validateAndPrepareFile,
} from "@/lib/security/file-validation";
import { FORM_SECURITY } from "@/lib/constants/form-security";
import { fetchWithTimeout } from "@/lib/fetch-with-timeout";
import { convexMutation } from "@/lib/convex-client";
import { corsHeaders, corsResponse } from "@/lib/cors";

// CORS headers for cross-origin requests
const corsHeadersMap = corsHeaders("POST, OPTIONS");

// Zod Schema for validation
const contactFormSchema = z.object({
  name: z.string().trim().min(2, "Name must be at least 2 characters"),
  email: z.string().trim().email("Invalid email address"),
  phone: z
    .string()
    .optional()
    .refine((val) => {
      if (!val || val.trim() === "") return true;
      // Accept international format with + prefix and 8-15 digits
      const cleanPhone = val.replace(/[\s\-\(\)]/g, "");
      return /^\+?[1-9]\d{7,14}$/.test(cleanPhone);
    }, "Invalid phone number format. Use international format (e.g., +974...)"),
  company: z.string().optional(),
  topic: z.enum(
    [
      "sales",
      "support",
      "billing",
      "partnerships",
      "press",
      "careers",
      "security",
      "demo",
      "other",
    ],
    {
      errorMap: () => ({ message: "Invalid inquiry type" }),
    },
  ),
  message: z
    .string()
    .trim()
    .min(10, "Message must be at least 10 characters")
    .max(10000, "Message must be less than 10,000 characters"),
  preferredLanguage: z.enum(["ar", "en"], {
    errorMap: () => ({ message: "Invalid language selection" }),
  }),
  consentGiven: z
    .boolean()
    .refine(
      (val) => val === true,
      "You must agree to the privacy policy and terms",
    ),
  recaptchaToken: z.string().optional().default(""),
});

type SafeAttachment = {
  buffer: Buffer;
  name: string;
  size: number;
  type: string;
};

const ALLOWED_CONTACT_MIME_TYPES = new Set(FILE_UPLOAD_CONFIG.allowedMimeTypes);

/**
 * Verify reCAPTCHA v3 token with Google
 *
 * In development mode (NODE_ENV !== 'production'), reCAPTCHA verification is bypassed
 * to allow testing without real tokens.
 */
async function verifyRecaptcha(
  token: string,
): Promise<{ success: boolean; score: number }> {
  const secretKey = process.env.RECAPTCHA_SECRET_KEY;
  const threshold = parseFloat(process.env.RECAPTCHA_SCORE_THRESHOLD || "0.5");
  const isDevelopment = process.env.NODE_ENV !== "production";

  // Development mode bypass - allows testing without real reCAPTCHA
  if (isDevelopment) {
    logger.info("[DEV] reCAPTCHA verification bypassed in development mode");
    return { success: true, score: 1.0 };
  }

  if (!secretKey) {
    logger.error("RECAPTCHA_SECRET_KEY not configured");
    return { success: false, score: 0 };
  }

  try {
    const response = await fetchWithTimeout(
      "https://www.google.com/recaptcha/api/siteverify",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },
        body: `secret=${secretKey}&response=${token}`,
      },
      5000,
    );

    const data = await response.json();

    return {
      success: data.success && data.score >= threshold,
      score: data.score || 0,
    };
  } catch (error) {
    const message = error instanceof Error ? error.message : "Unknown error";
    logger.error("reCAPTCHA verification failed", { error: message });
    return { success: false, score: 0 };
  }
}

/**
 * Sanitize text inputs to prevent XSS
 */
function sanitizeInput(input: string): string {
  return DOMPurify.sanitize(input, { ALLOWED_TAGS: [] }).trim();
}

/**
 * Upload file to Convex Storage
 */
async function uploadFile(
  file: SafeAttachment,
  submissionId: string,
): Promise<{
  storageId: string;
  url: string | null;
  name: string;
  size: number;
  type: string;
  bucket: string;
} | null> {
  try {
    // 1. Generate upload URL from Convex
    const uploadUrl = await convexMutation(
      "storage/mutations:generateUploadUrl",
      {},
    );
    if (!uploadUrl) {
      logger.error("Failed to generate Convex upload URL");
      return null;
    }

    // 2. Upload file to Convex storage
    const uploadResult = await fetch(uploadUrl as string, {
      method: "POST",
      headers: { "Content-Type": file.type },
      body: file.buffer,
    });

    if (!uploadResult.ok) {
      logger.error("Convex file upload failed", {
        status: uploadResult.status,
      });
      return null;
    }

    const { storageId } = await uploadResult.json();

    // 3. Save metadata
    await convexMutation("storage/mutations:saveFileMetadata", {
      storageId,
      fileName: file.name,
      fileType: file.type,
      fileSize: file.size,
      bucket: "contact-attachments",
      entityId: submissionId,
      entityType: "contact_submission",
    });

    // 4. Build permanent URL (no expiry unlike Supabase signed URLs)
    const url = `${process.env.NEXT_PUBLIC_CONVEX_URL?.replace(".cloud", ".site")}/getFile?storageId=${storageId}`;

    return {
      storageId: storageId as string,
      url,
      name: file.name,
      size: file.size,
      type: file.type,
      bucket: "contact-attachments",
    };
  } catch (error) {
    const message = error instanceof Error ? error.message : "Unknown error";
    logger.error("Unexpected upload error", { error: message });
    return null;
  }
}

/**
 * POST /api/contact/submit
 * Handle contact form submission with validation, file uploads, and security
 */
export const POST = withSecureRoute(
  async (request: NextRequest) => {
    try {
      const ip = getClientIp(request);
      const formData = await request.formData();

      if (process.env.NODE_ENV !== "production") {
        // Debug: Log all form fields to identify honeypot issues (dev only)
        const allFields: Record<string, string> = {};
        formData.forEach((value, key) => {
          if (typeof value === "string") {
            allFields[key] =
              value.length > 50 ? `${value.substring(0, 50)}...` : value;
          } else {
            allFields[key] = "[File]";
          }
        });
        logger.info("Contact form fields received", {
          fields: Object.keys(allFields),
          values: allFields,
        });
      }

      // Check both old and new honeypot field names
      const honeypotNew =
        (formData.get("hp_field_x7k9") as string | null)?.trim() || "";
      const honeypotOld =
        (formData.get("website") as string | null)?.trim() || "";
      const honeypotValue = honeypotNew || honeypotOld;

      if (honeypotOld) {
        logger.warn(
          'Old honeypot field "website" received - client may be using cached code',
          { value: honeypotOld },
        );
      }
      const formStartedAtRaw = formData.get("formStartedAt");
      const timeOnPageRaw = formData.get("timeOnPageMs");
      const parsedStart = formStartedAtRaw ? Number(formStartedAtRaw) : NaN;
      const parsedDuration = timeOnPageRaw ? Number(timeOnPageRaw) : NaN;
      const calculatedDuration = Number.isNaN(parsedDuration)
        ? Number.isNaN(parsedStart)
          ? NaN
          : Math.max(0, Date.now() - parsedStart)
        : parsedDuration;

      if (honeypotValue) {
        logger.warn("Contact form honeypot triggered", { ip, honeypotValue });
        return NextResponse.json(
          { error: "Invalid submission detected" },
          { status: 400, headers: corsHeadersMap },
        );
      }

      const hasTimingMetadata = !Number.isNaN(calculatedDuration);

      if (!hasTimingMetadata) {
        logger.warn("Contact form missing timing metadata", { ip });
      } else {
        if (calculatedDuration < FORM_SECURITY.MIN_DURATION_MS) {
          logger.warn("Contact form submitted too quickly", {
            ip,
            timeOnPageMs: calculatedDuration,
          });
          return NextResponse.json(
            { error: "Submission blocked. Please try again." },
            { status: 400, headers: corsHeadersMap },
          );
        }

        if (calculatedDuration < FORM_SECURITY.SUSPICIOUS_DURATION_MS) {
          logger.info("Contact form submitted unusually fast", {
            ip,
            timeOnPageMs: calculatedDuration,
          });
        }
      }

      const rawData = {
        name: formData.get("name") as string,
        email: formData.get("email") as string,
        phone: formData.get("phone") as string,
        company: formData.get("company") as string | undefined,
        topic: formData.get("topic") as string,
        message: formData.get("message") as string,
        preferredLanguage: formData.get("preferredLanguage") as "ar" | "en",
        consentGiven: formData.get("consentGiven") === "true",
        recaptchaToken: formData.get("recaptchaToken") as string,
      };

      const validationResult = contactFormSchema.safeParse(rawData);

      if (!validationResult.success) {
        const errors = validationResult.error.errors.map((err) => ({
          field: err.path[0] as string,
          message: err.message,
        }));

        return NextResponse.json(
          { error: "Validation failed", errors },
          { status: 400 },
        );
      }

      const validatedData = validationResult.data;

      // Verify reCAPTCHA if token is provided
      // In development or when reCAPTCHA is not configured, this is bypassed
      let recaptchaResult = { success: true, score: 1.0 };
      if (validatedData.recaptchaToken) {
        recaptchaResult = await verifyRecaptcha(validatedData.recaptchaToken);
        if (!recaptchaResult.success) {
          logger.warn("reCAPTCHA verification failed", {
            ip,
            score: recaptchaResult.score,
          });
          return NextResponse.json(
            { error: "Bot verification failed. Please try again." },
            { status: 400, headers: corsHeadersMap },
          );
        }
      } else {
        // No token provided - only allow in development
        const isDevelopment = process.env.NODE_ENV !== "production";
        if (!isDevelopment) {
          logger.warn("reCAPTCHA token missing in production", { ip });
          return NextResponse.json(
            { error: "reCAPTCHA verification required" },
            { status: 400, headers: corsHeadersMap },
          );
        }
        logger.info("[DEV] reCAPTCHA bypassed - no token provided");
      }

      const sanitizedData = {
        name: sanitizeInput(validatedData.name),
        email: sanitizeInput(validatedData.email),
        phone: validatedData.phone ? sanitizeInput(validatedData.phone) : "",
        company: validatedData.company
          ? sanitizeInput(validatedData.company)
          : null,
        topic: sanitizeInput(validatedData.topic),
        message: sanitizeInput(validatedData.message),
        preferredLanguage: validatedData.preferredLanguage,
        consentGiven: validatedData.consentGiven,
      };

      const files: File[] = [];
      let fileIndex = 0;
      while (true) {
        const file = formData.get(`file_${fileIndex}`) as File | null;
        if (!file) break;
        files.push(file);
        fileIndex++;
      }

      if (files.length > FILE_UPLOAD_CONFIG.maxFiles) {
        return NextResponse.json(
          {
            error: `Too many files. Maximum is ${FILE_UPLOAD_CONFIG.maxFiles} files`,
          },
          { status: 400 },
        );
      }

      const validatedFiles: SafeAttachment[] = [];

      for (const file of files) {
        const validation = await validateAndPrepareFile(
          file,
          ALLOWED_CONTACT_MIME_TYPES,
          FILE_UPLOAD_CONFIG.maxSizeBytes,
        );
        if (!validation.ok) {
          const errorMessage = validation.error.includes(file.name)
            ? validation.error
            : `File "${file.name}" failed validation: ${validation.error}`;
          return NextResponse.json(
            { error: errorMessage },
            { status: 400, headers: corsHeadersMap },
          );
        }

        const threat = detectSuspiciousContent(
          validation.buffer,
          validation.mime,
        );
        if (threat) {
          return NextResponse.json(
            { error: `Attachment "${file.name}" was rejected: ${threat}` },
            { status: 400, headers: corsHeadersMap },
          );
        }

        validatedFiles.push({
          buffer: validation.buffer,
          name: file.name,
          size: validation.size,
          type: validation.mime,
        });
      }

      const attachments: Array<{
        name: string;
        size: number;
        type: string;
        storageId: string;
        url: string | null;
        bucket: string;
      }> = [];
      const submissionId = crypto.randomUUID(); // Generate ID upfront

      if (validatedFiles.length > 0) {
        for (const file of validatedFiles) {
          const uploadResult = await uploadFile(file, submissionId);
          if (!uploadResult) {
            logger.error("Failed to upload file", undefined, {
              fileName: file.name,
            });
            return NextResponse.json(
              {
                error: `Failed to upload file: ${file.name}. Please try again.`,
              },
              { status: 500 },
            );
          }
          attachments.push(uploadResult);
        }

        const validatedAttachments = attachments.map((att) => ({
          name: sanitizeInput(att.name).substring(0, 255),
          size: typeof att.size === "number" && att.size > 0 ? att.size : 0,
          type: sanitizeInput(att.type).substring(0, 100),
          storageId: att.storageId,
          url: att.url,
          bucket: att.bucket,
        }));

        attachments.splice(0, attachments.length, ...validatedAttachments);
      }

      logger.info("About to insert contact submission", { submissionId });

      let submission: { id: string };
      try {
        const convexId = await convexMutation(
          "support/mutations:createContactSubmission",
          {
            name: sanitizedData.name,
            email: sanitizedData.email,
            phone: sanitizedData.phone,
            company: sanitizedData.company ?? undefined,
            topic: sanitizedData.topic,
            message: sanitizedData.message,
            preferred_language: sanitizedData.preferredLanguage,
            consent: sanitizedData.consentGiven,
            ip_address: ip,
            user_agent: request.headers.get("user-agent") || undefined,
            submission_language: sanitizedData.preferredLanguage,
            attachments: attachments.length > 0 ? attachments : undefined,
          },
        );
        submission = { id: convexId as string };
        logger.info("Contact saved to Convex", { id: submission.id });
      } catch (insertError) {
        const errorMsg = getErrorMessage(insertError);
        const errorCause =
          insertError &&
          typeof insertError === "object" &&
          "cause" in insertError
            ? (insertError as { cause?: unknown }).cause
            : undefined;
        logger.error("Exception during insert", errorCause, {
          errorMessage: errorMsg,
        });

        if (attachments.length > 0) {
          for (const att of attachments) {
            try {
              await convexMutation("storage/mutations:deleteFile", {
                storageId: att.storageId,
              });
            } catch (err: unknown) {
              logger.error("Rollback file deletion failed", err);
            }
          }
        }

        return NextResponse.json(
          { error: "Failed to submit contact form. Please try again." },
          { status: 500 },
        );
      }

      logger.info("Contact form submitted", {
        id: submission.id,
        topic: sanitizedData.topic,
        attachmentCount: attachments.length,
        recaptchaScore: recaptchaResult.score,
      });

      sendContactThankYouEmail(
        sanitizedData.email,
        sanitizedData.name,
        sanitizedData.topic,
        sanitizedData.preferredLanguage,
      )
        .then((sent) => {
          if (sent) {
            logger.info("Thank-you email sent for submission", {
              submissionId: submission.id,
            });
          } else {
            logger.warn("Failed to send thank-you email for submission", {
              submissionId: submission.id,
            });
          }
        })
        .catch((err) => {
          logger.error("Error sending thank-you email", {
            error: err,
            submissionId: submission.id,
          });
        });

      sendStaffContactNotification({
        submissionId: submission.id,
        name: sanitizedData.name,
        email: sanitizedData.email,
        phone: sanitizedData.phone || undefined,
        company: sanitizedData.company,
        topic: sanitizedData.topic,
        message: sanitizedData.message,
        preferredLanguage: sanitizedData.preferredLanguage,
      })
        .then((sent) => {
          if (sent) {
            logger.info("Staff notification sent for submission", {
              submissionId: submission.id,
            });
          } else {
            logger.warn("Failed to send staff notification for submission", {
              submissionId: submission.id,
            });
          }
        })
        .catch((err) => {
          logger.error("Error sending staff notification", {
            error: err,
            submissionId: submission.id,
          });
        });

      return NextResponse.json(
        {
          success: true,
          submissionId: submission.id,
          message:
            "Your message has been sent successfully. We'll get back to you within 24 hours.",
        },
        { status: 200, headers: corsHeadersMap },
      );
    } catch (error) {
      const message = error instanceof Error ? error.message : "Unknown error";
      logger.error("Unexpected error in contact form submission", {
        error: message,
      });

      return NextResponse.json(
        {
          error:
            "An unexpected error occurred. Our team has been notified. Please try again later.",
        },
        { status: 500 },
      );
    }
  },
  { parseJson: false, rateLimiter: rateLimiters.contact },
);

/**
 * OPTIONS /api/contact/submit
 * Handle preflight requests
 */
export async function OPTIONS() {
  return corsResponse("POST, OPTIONS");
}

/**
 * GET /api/contact/submit
 * Method not allowed
 */
export async function GET() {
  return NextResponse.json(
    { error: "Method not allowed" },
    { status: 405, headers: corsHeadersMap },
  );
}
