import { useState, useEffect, useCallback } from "react";
import { supabase } from "@/integrations/supabase/client";
import { useAuth } from "@/contexts/AuthContext";
import { useToast } from "@/hooks/use-toast";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Separator } from "@/components/ui/separator";
import { Checkbox } from "@/components/ui/checkbox";
import { ThumbsUp, ThumbsDown, MessageSquare, Loader2, Send } from "lucide-react";
import { format } from "date-fns";

interface Props {
  entityType: string;
  entityId: string;
  showVoting?: boolean;
  locked?: boolean;
  lockedMessage?: string;
  /** When true, only comments flagged visible_to_owner (plus the viewer's own) are shown. */
  restrictToOwnerVisible?: boolean;
  /** When true, render a "Visible to owner" checkbox in the composer. */
  showOwnerVisibilityToggle?: boolean;
}

type UserProfile = {
  user_id: string;
  full_name: string | null;
  email: string | null;
};

const getProfileLabel = (profile?: UserProfile | null) =>
  profile?.full_name || profile?.email || "User";

export default function VotingAndComments({
  entityType,
  entityId,
  showVoting = true,
  locked = false,
  lockedMessage,
  restrictToOwnerVisible = false,
  showOwnerVisibilityToggle = false,
}: Props) {
  const { user } = useAuth();
  const { toast } = useToast();
  const [votes, setVotes] = useState<any[]>([]);
  const [comments, setComments] = useState<any[]>([]);
  const [newComment, setNewComment] = useState("");
  const [visibleToOwner, setVisibleToOwner] = useState(false);
  const [loadingVote, setLoadingVote] = useState(false);
  const [loadingComment, setLoadingComment] = useState(false);

  const fetchProfilesMap = useCallback(async (userIds: string[]) => {
    const uniqueUserIds = [...new Set(userIds.filter(Boolean))];

    if (!uniqueUserIds.length) {
      return new Map<string, UserProfile>();
    }

    const { data, error } = await supabase
      .from("profiles")
      .select("user_id, full_name, email")
      .in("user_id", uniqueUserIds);

    if (error) throw error;

    return new Map((data ?? []).map((profile) => [profile.user_id, profile as UserProfile]));
  }, []);

  const fetchVotes = useCallback(async () => {
    try {
      const { data, error } = await supabase
        .from("entity_votes")
        .select("*")
        .eq("entity_type", entityType)
        .eq("entity_id", entityId);

      if (error) throw error;

      const profileMap = await fetchProfilesMap((data ?? []).map((vote) => vote.user_id));
      setVotes(
        (data ?? []).map((vote) => ({
          ...vote,
          profile: profileMap.get(vote.user_id) ?? null,
        }))
      );
    } catch (error) {
      console.error("Failed to load votes", error);
      setVotes([]);
    }
  }, [entityId, entityType, fetchProfilesMap]);

  const fetchComments = useCallback(async () => {
    try {
      const { data, error } = await supabase
        .from("entity_comments")
        .select("*")
        .eq("entity_type", entityType)
        .eq("entity_id", entityId)
        .order("created_at", { ascending: true });

      if (error) throw error;

      const profileMap = await fetchProfilesMap((data ?? []).map((comment) => comment.user_id));
      setComments(
        (data ?? []).map((comment) => ({
          ...comment,
          profile: profileMap.get(comment.user_id) ?? null,
        }))
      );
    } catch (error) {
      console.error("Failed to load comments", error);
      setComments([]);
    }
  }, [entityId, entityType, fetchProfilesMap]);

  useEffect(() => {
    if (!entityId) return;

    void Promise.all([showVoting ? fetchVotes() : Promise.resolve(), fetchComments()]);

    const channel = supabase
      .channel(`voting-comments-${entityType}-${entityId}`)
      .on(
        "postgres_changes",
        {
          event: "*",
          schema: "public",
          table: "entity_comments",
          filter: `entity_id=eq.${entityId}`,
        },
        () => {
          void fetchComments();
        }
      )
      .subscribe();

    return () => {
      void supabase.removeChannel(channel);
    };
  }, [entityId, entityType, fetchVotes, fetchComments, showVoting]);

  const myVote = votes.find((v) => v.user_id === user?.id);
  const approveCount = votes.filter((v) => v.vote === "approve").length;
  const denyCount = votes.filter((v) => v.vote === "deny").length;

  const handleVote = async (vote: "approve" | "deny") => {
    if (!user) return;

    setLoadingVote(true);
    try {
      if (myVote?.vote === vote) {
        const { error } = await supabase.from("entity_votes").delete().eq("id", myVote.id);
        if (error) throw error;
      } else {
        const { error } = await supabase.from("entity_votes").upsert(
          {
            entity_type: entityType,
            entity_id: entityId,
            user_id: user.id,
            vote,
          },
          { onConflict: "entity_type,entity_id,user_id" }
        );
        if (error) throw error;
      }

      await fetchVotes();
    } catch (err: any) {
      toast({ title: "Error", description: err.message, variant: "destructive" });
    } finally {
      setLoadingVote(false);
    }
  };

  const handleComment = async () => {
    if (!user || !newComment.trim()) return;

    setLoadingComment(true);
    try {
      const { error } = await supabase.from("entity_comments").insert({
        entity_type: entityType,
        entity_id: entityId,
        user_id: user.id,
        comment: newComment.trim(),
        visible_to_owner: showOwnerVisibilityToggle ? visibleToOwner : false,
      });

      if (error) throw error;

      setNewComment("");
      setVisibleToOwner(false);
      await fetchComments();
    } catch (err: any) {
      toast({ title: "Error", description: err.message, variant: "destructive" });
    } finally {
      setLoadingComment(false);
    }
  };

  const visibleComments = restrictToOwnerVisible
    ? comments.filter((c) => c.visible_to_owner || c.user_id === user?.id)
    : comments;

  return (
    <div className="space-y-5">
      <Separator />
      {locked && (
        <div className="rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800">
          {lockedMessage || "This item has been finalized and is locked. No further votes or comments can be added."}
        </div>
      )}
      {showVoting && (
        <>
          <div>
            <h4 className="font-semibold text-sm mb-3 flex items-center gap-2">
              <ThumbsUp className="w-4 h-4" /> Vote
            </h4>
            <div className="flex items-center gap-3 mb-3">
              <Button
                variant={myVote?.vote === "approve" ? "default" : "outline"}
                size="sm"
                onClick={() => handleVote("approve")}
                disabled={loadingVote || locked}
                className="gap-2"
              >
                <ThumbsUp className="w-4 h-4" /> Approve ({approveCount})
              </Button>
              <Button
                variant={myVote?.vote === "deny" ? "destructive" : "outline"}
                size="sm"
                onClick={() => handleVote("deny")}
                disabled={loadingVote || locked}
                className="gap-2"
              >
                <ThumbsDown className="w-4 h-4" /> Deny ({denyCount})
              </Button>
              {myVote && !locked && <span className="text-xs text-muted-foreground">You voted to {myVote.vote}. Click again to retract.</span>}
            </div>
            {votes.length > 0 && (
              <div className="flex flex-wrap gap-2">
                {votes.map((v) => (
                  <Badge key={v.id} variant={v.vote === "approve" ? "default" : "destructive"} className="text-xs">
                    {v.voter_name || getProfileLabel(v.profile)}: {v.vote}
                  </Badge>
                ))}
              </div>
            )}
          </div>

          <Separator />
        </>
      )}

      <div>
        <h4 className="font-semibold text-sm mb-3 flex items-center gap-2">
          <MessageSquare className="w-4 h-4" /> Comments ({visibleComments.length})
        </h4>
        <div className="space-y-3 max-h-60 overflow-y-auto mb-3">
          {visibleComments.length === 0 && <p className="text-sm text-muted-foreground">No comments yet.</p>}
          {visibleComments.map((c) => (
            <div key={c.id} className="bg-muted/50 rounded-lg p-3 border">
              <div className="flex items-center justify-between mb-1">
                <div className="flex items-center gap-2">
                  <span className="text-sm font-medium">{getProfileLabel(c.profile)}</span>
                  {showOwnerVisibilityToggle && c.visible_to_owner && (
                    <Badge variant="outline" className="text-[10px] py-0 px-1.5 border-emerald-300 text-emerald-700 bg-emerald-50">
                      Visible to owner
                    </Badge>
                  )}
                </div>
                <span className="text-xs text-muted-foreground">{format(new Date(c.created_at), "MMM d, yyyy h:mm a")}</span>
              </div>
              <p className="text-sm whitespace-pre-wrap">{c.comment}</p>
            </div>
          ))}
        </div>
        {!locked && (
          <div className="space-y-2">
            <div className="flex gap-2">
              <Textarea
                value={newComment}
                onChange={(e) => setNewComment(e.target.value)}
                placeholder="Add a comment..."
                className="min-h-[60px]"
              />
              <Button size="sm" onClick={handleComment} disabled={loadingComment || !newComment.trim()} className="self-end">
                {loadingComment ? <Loader2 className="w-4 h-4 animate-spin" /> : <Send className="w-4 h-4" />}
              </Button>
            </div>
            {showOwnerVisibilityToggle && (
              <label className="flex items-center gap-2 text-xs text-muted-foreground cursor-pointer select-none">
                <Checkbox
                  checked={visibleToOwner}
                  onCheckedChange={(v) => setVisibleToOwner(!!v)}
                />
                Visible to owner (otherwise this comment is internal to staff &amp; committee)
              </label>
            )}
          </div>
        )}
      </div>
    </div>
  );
}