"use client";

import { useCallback, useEffect, useState, type ChangeEvent, type FormEvent } from "react";
import Image from "next/image";
import { Avatar } from "@/components/avatar";
import { IconCamera, IconClose } from "@/components/icons";
import { resizeImageToBlob } from "@/lib/image";
import { createClient, waitForRealtimeAuth } from "@/lib/supabase/client";
import type { MyReactionRow, ReactionCountRow, WallHistoryRow, WallNoteRow } from "./page";

const MAX_LENGTH = 200;
// Les Archontes peuvent désormais publier aussi (plus réservé aux
// Citoyens) : 12 places pour couvrir tout le groupe, pas seulement les
// Citoyens — doit rester aligné avec la borne côté serveur dans
// post_wall_note (supabase/schema.sql).
const MAX_NOTES_PER_DAY = 12;
const MAX_IMAGE_SOURCE_BYTES = 20 * 1024 * 1024;
const REACTION_EMOJIS = ["👍", "😂", "😱", "❤️", "🔥"];

// Rotation déterministe par note (jamais Math.random() au rendu, cf. la
// même contrainte déjà rencontrée cette session avec les hooks React) —
// donne l'effet "punaisé en vrac" sans risquer un hydration mismatch.
function rotationFor(id: string): number {
  let hash = 0;
  for (let i = 0; i < id.length; i++) hash = (hash * 31 + id.charCodeAt(i)) | 0;
  return (Math.abs(hash) % 7) - 3;
}

function formatDateTime(iso: string): string {
  return new Date(iso).toLocaleString("fr-FR", {
    dateStyle: "short",
    timeStyle: "short",
    timeZone: "Europe/Paris",
  });
}

// Clé de date locale (Europe/Paris) au format sv-SE (YYYY-MM-DD, triable),
// même pattern que calendrier/day-card.tsx.
function localDateKey(iso: string): string {
  return new Date(iso).toLocaleDateString("sv-SE", { timeZone: "Europe/Paris" });
}

function formatDateLabel(dateKey: string): string {
  const label = new Date(`${dateKey}T12:00:00`).toLocaleDateString("fr-FR", {
    weekday: "long",
    day: "numeric",
    month: "long",
    timeZone: "Europe/Paris",
  });
  return label.charAt(0).toUpperCase() + label.slice(1);
}

function buildReactionCounts(rows: ReactionCountRow[]): Record<string, Record<string, number>> {
  const map: Record<string, Record<string, number>> = {};
  for (const row of rows) {
    if (!map[row.note_id]) map[row.note_id] = {};
    map[row.note_id][row.emoji] = row.count;
  }
  return map;
}

function buildMyReactions(rows: MyReactionRow[]): Record<string, Set<string>> {
  const map: Record<string, Set<string>> = {};
  for (const row of rows) {
    if (!map[row.note_id]) map[row.note_id] = new Set();
    map[row.note_id].add(row.emoji);
  }
  return map;
}

function ReactionBar({
  counts,
  mine,
  onToggle,
}: {
  counts: Record<string, number>;
  mine: Set<string>;
  onToggle: (emoji: string) => void;
}) {
  return (
    <div className="flex flex-wrap gap-1">
      {REACTION_EMOJIS.map((emoji) => {
        const count = counts[emoji] ?? 0;
        const active = mine.has(emoji);
        return (
          <button
            key={emoji}
            type="button"
            onClick={() => onToggle(emoji)}
            className={`flex items-center gap-0.5 rounded-full border px-1.5 py-0.5 text-xs transition ${
              active ? "border-gold bg-gold/20" : "border-gold/20 hover:bg-gold/10"
            }`}
          >
            <span>{emoji}</span>
            {count > 0 && <span className="text-[0.6rem] text-text-mut">{count}</span>}
          </button>
        );
      })}
    </div>
  );
}

function NoteCard({
  note,
  reactionCounts,
  myReactions,
  onToggleReaction,
}: {
  note: WallNoteRow;
  reactionCounts: Record<string, number>;
  myReactions: Set<string>;
  onToggleReaction: (emoji: string) => void;
}) {
  return (
    <div
      className="marble-surface flex min-h-[7rem] flex-col justify-between gap-2 rounded-lg border border-gold/30 p-3 shadow-md"
      style={{ transform: `rotate(${rotationFor(note.id)}deg)` }}
    >
      <div className="flex flex-col gap-2">
        {note.image_url && (
          <Image
            src={note.image_url}
            alt=""
            width={300}
            height={200}
            className="max-h-32 w-full rounded-md object-cover"
          />
        )}
        <p className="text-sm text-text-marble">{note.text}</p>
      </div>
      <div className="flex items-center justify-between gap-2">
        <ReactionBar counts={reactionCounts} mine={myReactions} onToggle={onToggleReaction} />
        <span className="h-2 w-2 shrink-0 rounded-full bg-oxblood" aria-hidden />
      </div>
    </div>
  );
}

export function MurView({
  isJudge,
  currentUserId,
  initialNotes,
  initialMyNoteToday,
  initialHistory,
  initialReactionCounts,
  initialMyReactions,
}: {
  isJudge: boolean;
  currentUserId: string;
  initialNotes: WallNoteRow[];
  initialMyNoteToday: WallNoteRow | null;
  initialHistory: WallHistoryRow[];
  initialReactionCounts: ReactionCountRow[];
  initialMyReactions: MyReactionRow[];
}) {
  const [notes, setNotes] = useState(initialNotes);
  const [myNoteToday, setMyNoteToday] = useState(initialMyNoteToday);
  const [history, setHistory] = useState(initialHistory);
  const [reactionCounts, setReactionCounts] = useState(() => buildReactionCounts(initialReactionCounts));
  const [myReactions, setMyReactions] = useState(() => buildMyReactions(initialMyReactions));
  const [text, setText] = useState("");
  const [imageFile, setImageFile] = useState<File | null>(null);
  const [imagePreview, setImagePreview] = useState<string | null>(null);
  const [imageError, setImageError] = useState<string | null>(null);
  const [posting, setPosting] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [historyDateFilter, setHistoryDateFilter] = useState("all");

  const refetch = useCallback(async () => {
    const supabase = createClient();
    const [{ data: todayNotes }, { data: myNotes }, { data: hist }, { data: counts }, { data: mine }] =
      await Promise.all([
        supabase.rpc("wall_notes_today"),
        isJudge
          ? Promise.resolve({ data: null })
          : supabase
              .from("wall_notes")
              .select("id, text, image_url, created_at")
              .order("created_at", { ascending: false }),
        isJudge
          ? supabase
              .from("wall_notes")
              .select("id, user_id, text, image_url, created_at, profiles(pseudo, avatar_url)")
              .order("created_at", { ascending: false })
              .returns<WallHistoryRow[]>()
          : Promise.resolve({ data: null }),
        supabase.rpc("wall_note_reaction_counts"),
        supabase.from("wall_note_reactions").select("note_id, emoji").eq("user_id", currentUserId),
      ]);
    if (todayNotes) setNotes(todayNotes);
    if (myNotes) {
      const today = new Date().toLocaleDateString("sv-SE", { timeZone: "Europe/Paris" });
      setMyNoteToday(
        myNotes.find(
          (n) => new Date(n.created_at).toLocaleDateString("sv-SE", { timeZone: "Europe/Paris" }) === today,
        ) ?? null,
      );
    }
    if (hist) setHistory(hist);
    if (counts) setReactionCounts(buildReactionCounts(counts));
    if (mine) setMyReactions(buildMyReactions(mine));
  }, [isJudge, currentUserId]);

  useEffect(() => {
    const supabase = createClient();
    let channel: ReturnType<typeof supabase.channel> | null = null;
    let cancelled = false;

    waitForRealtimeAuth(supabase).then(() => {
      if (cancelled) return;
      channel = supabase
        .channel("wall-notes-changes")
        .on("postgres_changes", { event: "*", schema: "public", table: "wall_notes" }, refetch)
        .on("postgres_changes", { event: "*", schema: "public", table: "wall_note_reactions" }, refetch)
        .subscribe();
    });

    return () => {
      cancelled = true;
      if (channel) supabase.removeChannel(channel);
    };
  }, [refetch]);

  function handleImageChange(event: ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0];
    event.target.value = "";
    if (!file) return;

    if (!file.type.startsWith("image/")) {
      setImageError("Choisis une image.");
      return;
    }
    if (file.size > MAX_IMAGE_SOURCE_BYTES) {
      setImageError("Image trop lourde, choisis-en une autre.");
      return;
    }

    setImageError(null);
    setImageFile(file);
    setImagePreview((current) => {
      if (current) URL.revokeObjectURL(current);
      return URL.createObjectURL(file);
    });
  }

  function clearImage() {
    setImageFile(null);
    setImagePreview((current) => {
      if (current) URL.revokeObjectURL(current);
      return null;
    });
    setImageError(null);
  }

  async function handleSubmit(event: FormEvent) {
    event.preventDefault();
    setError(null);

    const trimmed = text.trim();
    if (!trimmed) {
      setError("Écris une note avant de publier.");
      return;
    }

    setPosting(true);
    const supabase = createClient();

    let imageUrl: string | null = null;
    if (imageFile) {
      try {
        const blob = await resizeImageToBlob(imageFile);
        const path = `${crypto.randomUUID()}.webp`;
        const { error: uploadError } = await supabase.storage
          .from("wall-images")
          .upload(path, blob, { contentType: "image/webp" });
        if (uploadError) {
          setPosting(false);
          setError("Impossible d'envoyer l'image, réessaie.");
          return;
        }
        const { data: publicUrlData } = supabase.storage.from("wall-images").getPublicUrl(path);
        imageUrl = publicUrlData.publicUrl;
      } catch {
        setPosting(false);
        setError("Impossible de traiter l'image, réessaie.");
        return;
      }
    }

    const { error: rpcError } = await supabase.rpc("post_wall_note", {
      p_text: trimmed,
      p_image_url: imageUrl,
    });
    setPosting(false);

    if (rpcError) {
      setError(
        rpcError.message.includes("already posted")
          ? "Tu as déjà publié une note aujourd'hui."
          : rpcError.message.includes("wall full")
            ? "Le mur est déjà complet pour aujourd'hui."
            : "Une erreur est survenue, réessaie.",
      );
      refetch();
      return;
    }

    setText("");
    clearImage();
    refetch();
  }

  async function handleToggleReaction(noteId: string, emoji: string) {
    // Optimiste : le canal Realtime confirmera (ou corrigera) juste après.
    setMyReactions((current) => {
      const next = new Set(current[noteId] ?? []);
      const wasActive = next.has(emoji);
      if (wasActive) next.delete(emoji);
      else next.add(emoji);
      return { ...current, [noteId]: next };
    });
    setReactionCounts((current) => {
      const wasActive = myReactions[noteId]?.has(emoji) ?? false;
      const noteCounts = { ...(current[noteId] ?? {}) };
      noteCounts[emoji] = Math.max(0, (noteCounts[emoji] ?? 0) + (wasActive ? -1 : 1));
      return { ...current, [noteId]: noteCounts };
    });

    const supabase = createClient();
    const { error: rpcError } = await supabase.rpc("toggle_wall_note_reaction", {
      p_note_id: noteId,
      p_emoji: emoji,
    });
    if (rpcError) refetch();
  }

  const wallFull = notes.length >= MAX_NOTES_PER_DAY;

  const historyDates = Array.from(new Set(history.map((n) => localDateKey(n.created_at)))).sort((a, b) =>
    b.localeCompare(a),
  );
  const filteredHistory =
    historyDateFilter === "all" ? history : history.filter((n) => localDateKey(n.created_at) === historyDateFilter);

  return (
    <div className="flex flex-col gap-6">
      <div>
        <p className="mb-3 text-center text-xs font-medium tracking-wide text-marble/60 uppercase">
          {notes.length} / {MAX_NOTES_PER_DAY}{" "}
          aujourd&apos;hui
        </p>
        {notes.length === 0 ? (
          <p className="text-center text-sm text-marble/60">Le mur est encore vide aujourd&apos;hui.</p>
        ) : (
          <div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4">
            {notes.map((note) => (
              <NoteCard
                key={note.id}
                note={note}
                reactionCounts={reactionCounts[note.id] ?? {}}
                myReactions={myReactions[note.id] ?? new Set()}
                onToggleReaction={(emoji) => handleToggleReaction(note.id, emoji)}
              />
            ))}
          </div>
        )}
      </div>

      <div className="marble-surface rounded-2xl border border-gold/40 p-4 shadow-sm">
        {myNoteToday ? (
          <div>
            <p className="text-sm text-olive">Ta note d&apos;aujourd&apos;hui est publiée.</p>
            {myNoteToday.image_url && (
              <Image
                src={myNoteToday.image_url}
                alt=""
                width={300}
                height={200}
                className="mt-2 max-h-40 w-full rounded-md object-cover"
              />
            )}
            <p className="mt-2 rounded-md border border-gold/20 bg-white/40 px-3 py-2 text-sm text-text-marble">
              {myNoteToday.text}
            </p>
            <p className="mt-2 text-xs text-text-mut">Reviens demain pour en publier une nouvelle.</p>
          </div>
        ) : wallFull ? (
          <p className="text-sm text-text-mut">Le mur est complet pour aujourd&apos;hui, reviens demain !</p>
        ) : (
          <form onSubmit={handleSubmit} className="flex flex-col gap-2">
            <textarea
              value={text}
              onChange={(event) => setText(event.target.value)}
              maxLength={MAX_LENGTH}
              rows={2}
              placeholder="Écrire une note anonyme…"
              className="rounded-md border border-gold/30 bg-white/50 px-3 py-2 text-sm text-text-marble outline-none focus:border-gold"
            />
            <p className="text-right text-[0.65rem] text-text-mut">
              {text.length}/{MAX_LENGTH}
            </p>

            {imagePreview ? (
              <div className="relative w-32">
                <Image
                  src={imagePreview}
                  alt=""
                  width={128}
                  height={96}
                  unoptimized
                  className="h-24 w-32 rounded-md object-cover"
                />
                <button
                  type="button"
                  onClick={clearImage}
                  className="absolute -top-2 -right-2 rounded-full bg-ink-2 p-1 text-marble"
                  aria-label="Retirer l'image"
                >
                  <IconClose className="h-3 w-3" />
                </button>
              </div>
            ) : (
              <label className="inline-flex w-fit cursor-pointer items-center gap-1.5 rounded-md border border-gold/30 px-3 py-1.5 text-xs font-medium text-text-marble hover:bg-gold/10">
                <IconCamera className="h-4 w-4" />
                Ajouter une image (optionnel)
                <input type="file" accept="image/*" onChange={handleImageChange} className="hidden" />
              </label>
            )}
            {imageError && <p className="text-xs text-oxblood">{imageError}</p>}

            {error && (
              <p role="alert" className="text-sm text-oxblood">
                {error}
              </p>
            )}
            <button
              type="submit"
              disabled={posting}
              className="rounded-md bg-ink-2 px-4 py-2 font-heading text-sm tracking-wide text-gold-bright uppercase transition hover:bg-ink disabled:opacity-50"
            >
              {posting ? "Publication…" : "Publier anonymement"}
            </button>
          </form>
        )}
      </div>

      {isJudge && (
        <div className="marble-surface rounded-2xl border border-gold/40 p-4 shadow-sm">
          <div className="mb-2 flex flex-wrap items-center justify-between gap-2">
            <p className="font-heading text-sm tracking-wide text-text-mut uppercase">Historique complet</p>
            {historyDates.length > 0 && (
              <select
                value={historyDateFilter}
                onChange={(event) => setHistoryDateFilter(event.target.value)}
                className="rounded-md border border-gold/30 bg-white/50 px-2 py-1 text-xs text-text-marble outline-none focus:border-gold"
              >
                <option value="all">Tous les jours</option>
                {historyDates.map((dateKey) => (
                  <option key={dateKey} value={dateKey}>
                    {formatDateLabel(dateKey)}
                  </option>
                ))}
              </select>
            )}
          </div>
          {filteredHistory.length === 0 ? (
            <p className="text-sm text-text-mut">
              {history.length === 0 ? "Aucune note publiée pour l'instant." : "Aucune note ce jour-là."}
            </p>
          ) : (
            <ul className="flex flex-col divide-y divide-gold/10">
              {filteredHistory.map((note) => (
                <li key={note.id} className="flex items-start gap-2 py-1.5 text-sm">
                  <Avatar
                    pseudo={note.profiles?.pseudo ?? "?"}
                    avatarUrl={note.profiles?.avatar_url ?? null}
                    size="sm"
                  />
                  <div className="min-w-0 flex-1">
                    <p className="text-xs font-medium text-text-mut">
                      {note.profiles?.pseudo ?? "un Citoyen"} — {formatDateTime(note.created_at)}
                    </p>
                    <p className="text-text-marble">{note.text}</p>
                    {note.image_url && (
                      <Image
                        src={note.image_url}
                        alt=""
                        width={200}
                        height={150}
                        className="mt-1 max-h-32 rounded-md object-cover"
                      />
                    )}
                  </div>
                </li>
              ))}
            </ul>
          )}
        </div>
      )}
    </div>
  );
}
