"use server";

import { headers } from "next/headers";
import { z } from "zod";
import { sendMail } from "@/lib/email/send";
import { verifyTurnstileToken } from "@/lib/security/turnstile";
import {
  contactNotificationHtml,
  contactNotificationSubject,
  contactNotificationText,
  contactConfirmationHtml,
  contactConfirmationSubject,
  contactConfirmationText,
  type ContactSubmission,
} from "@/lib/email/templates";
import type { ContactState } from "./state";

// === Validation ====================================================

const ReasonOptions = [
  "Demander un apercu personnalise de mon futur site",
  "Refaire mon site existant",
  "Creer un nouveau site",
  "Question sur l'offre / les tarifs",
  "Autre",
] as const;

const contactSchema = z.object({
  firstName: z.string().trim().min(1, "Prenom requis").max(80),
  lastName: z.string().trim().min(1, "Nom requis").max(80),
  company: z.string().trim().min(1, "Entreprise requise").max(120),
  email: z.string().trim().email("Email invalide").max(200),
  reason: z.enum(ReasonOptions).or(z.string().min(1).max(120)),
  message: z.string().trim().min(10, "Message trop court (10 caracteres minimum)").max(5000),
  // Honeypot : un bot remplira ce champ, un humain ne le verra pas.
  website: z.string().max(0, "spam").optional().default(""),
});

// === Rate limit en memoire (par IP) ================================
//
// Tres simple : Map<ip, [timestamps]> avec window glissante de 1 h, max 5
// submits. En memoire = reset au redeploy, mais c'est suffisant pour un volume
// faible. Si trafic croit : passer sur Redis/Upstash.

const RATE_WINDOW_MS = 60 * 60 * 1000; // 1 h
const RATE_MAX = 5;
const rateBucket = new Map<string, number[]>();

function checkRateLimit(ip: string): boolean {
  const now = Date.now();
  const bucket = rateBucket.get(ip) ?? [];
  const recent = bucket.filter((ts) => now - ts < RATE_WINDOW_MS);
  if (recent.length >= RATE_MAX) {
    rateBucket.set(ip, recent);
    return false;
  }
  recent.push(now);
  rateBucket.set(ip, recent);
  return true;
}

// === Server Action =================================================
// ContactState et initialContactState sont declares dans ./state.ts
// (un fichier "use server" ne peut exporter que des fonctions async).

export async function submitContactAction(
  _prev: ContactState,
  formData: FormData,
): Promise<ContactState> {
  // Recupere l'IP via les headers (Cloudflare CF-Connecting-IP en prod, sinon fallback).
  // En dev on a un IP local generique : pas grave pour le rate limit.
  const h = await headers();
  const ip =
    h.get("cf-connecting-ip") ??
    h.get("x-forwarded-for")?.split(",")[0]?.trim() ??
    h.get("x-real-ip") ??
    "unknown";

  if (!checkRateLimit(ip)) {
    return {
      status: "error",
      message: "Trop de demandes envoyees recemment. Reessayez dans quelques minutes.",
    };
  }

  // Verifie le token Cloudflare Turnstile (challenge anti-bot).
  // En l'absence de TURNSTILE_SECRET_KEY (dev local), retourne ok: true gracieusement.
  const turnstileToken = formData.get("cf-turnstile-response");
  const turnstile = await verifyTurnstileToken(
    typeof turnstileToken === "string" ? turnstileToken : null,
    ip !== "unknown" ? ip : undefined,
  );
  if (!turnstile.ok) {
    return {
      status: "error",
      message:
        turnstile.reason === "missing_token"
          ? "Verification anti-bot non completee. Rechargez la page et reessayez."
          : "Verification anti-bot echouee. Rechargez la page et reessayez.",
    };
  }

  // Parse + valide.
  const raw = Object.fromEntries(formData.entries());
  const parsed = contactSchema.safeParse(raw);
  if (!parsed.success) {
    // Honeypot rempli ? On simule un succes silencieux (ne pas reveler au bot).
    if (raw.website && typeof raw.website === "string" && raw.website.length > 0) {
      return { status: "success", message: "Message recu." };
    }
    const fieldErrors: ContactState["fieldErrors"] = {};
    const allowedKeys = ["firstName", "lastName", "company", "email", "reason", "message"] as const;
    type FieldKey = (typeof allowedKeys)[number];
    for (const issue of parsed.error.issues) {
      const path = issue.path[0];
      if (typeof path === "string" && (allowedKeys as readonly string[]).includes(path)) {
        fieldErrors[path as FieldKey] = issue.message;
      }
    }
    return {
      status: "error",
      message: "Le formulaire contient des erreurs.",
      fieldErrors,
    };
  }

  const submission: ContactSubmission = {
    firstName: parsed.data.firstName,
    lastName: parsed.data.lastName,
    company: parsed.data.company,
    email: parsed.data.email,
    reason: parsed.data.reason,
    message: parsed.data.message,
  };

  const notifyTo = process.env.CONTACT_NOTIFY_TO;
  if (!notifyTo) {
    console.error("[contact] CONTACT_NOTIFY_TO env manquante");
    return {
      status: "error",
      message: "Probleme technique cote serveur. Reessayez plus tard ou ecrivez directement par email.",
    };
  }

  // Envoi en parallele : notification interne + confirmation prospect.
  // On considere le submit reussi si au moins la notification interne est partie
  // (l'agence pourra recontacter meme si la confirmation au prospect rate).
  const [notifResult, confirmResult] = await Promise.all([
    sendMail({
      to: notifyTo,
      replyTo: submission.email,
      subject: contactNotificationSubject(submission),
      text: contactNotificationText(submission),
      html: contactNotificationHtml(submission),
    }),
    sendMail({
      to: submission.email,
      subject: contactConfirmationSubject(),
      text: contactConfirmationText(submission),
      html: contactConfirmationHtml(submission),
    }),
  ]);

  if (!notifResult.ok) {
    console.error("[contact] notification email failed:", notifResult.error);
    return {
      status: "error",
      message: "Le message n'a pas pu etre envoye. Reessayez ou ecrivez directement par email.",
    };
  }

  if (!confirmResult.ok) {
    // Pas bloquant : on log et on dit quand meme success cote user.
    console.warn("[contact] confirmation email to prospect failed:", confirmResult.error);
  }

  return {
    status: "success",
    message: "Message bien recu. Nous vous repondons rapidement.",
  };
}
