"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import { useSearchParams, useRouter, usePathname } from "next/navigation";
import {
  useChannel,
  useFeatureFlags,
  useMessages,
  useSdk,
  useTyping,
  type Message,
} from "@lynkd/messaging-inbox-sdk";
import { useUi } from "@/lib/ui-state";
import { Header } from "@/components/nav/Header";
import { MessageItem } from "./MessageItem";
import { Composer } from "./Composer";
import { LanguageMenu } from "./LanguageMenu";
import { Avatar, IconButton } from "@/components/ui/primitives";
import { groupByDay } from "@/lib/format";
import { Hash, Lock, Users, Video, Phone, Pin, Info, UserPlus, Languages } from "lucide-react";

export function ChannelView({ channelId }: { channelId: string }) {
  const channel = useChannel(channelId);
  const { messages, pinned, send, react, pin, save, edit, remove } = useMessages(channelId);
  const { userById, me, client, refresh } = useSdk();
  const flags = useFeatureFlags();
  const { openThread, startHuddle, openModal } = useUi();
  const typing = useTyping(channelId);
  const bottomRef = useRef<HTMLDivElement>(null);
  const scrollRef = useRef<HTMLDivElement>(null);
  const searchParams = useSearchParams();
  const jumpMsg = searchParams.get("msg");
  const router = useRouter();
  const pathname = usePathname();
  const [chatLang, setChatLang] = useState<string | null>(null);
  const [langOpen, setLangOpen] = useState(false);

  // reset the whole-chat translation when switching channels
  useEffect(() => { setChatLang(null); setLangOpen(false); }, [channelId]);

  // auto-scroll on channel change, and on new messages only if already near the bottom
  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "auto" });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [channelId]);
  useEffect(() => {
    const el = scrollRef.current;
    if (!el) return;
    const nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 140;
    if (nearBottom) bottomRef.current?.scrollIntoView({ behavior: "smooth" });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [messages.length]);

  // mark the channel read on open (clears the sidebar unread badge)
  useEffect(() => {
    if (!channelId) return;
    let alive = true;
    client.markRead(channelId).then(() => alive && refresh()).catch(() => {});
    return () => { alive = false; };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [channelId]);

  // deep-link jump: /c/:id?msg=<id> scrolls to + flashes that message. Keyed on
  // jumpMsg so jumping to a message in the channel you're already viewing (from
  // search / a notification) still re-fires. It runs ONCE per jump: as soon as
  // the target resolves (or we give up), the ?msg param is stripped so later
  // sends / incoming messages don't re-yank the viewport back to the old message.
  // gate on "messages have loaded" (a boolean) rather than the raw count, so
  // that later sends / incoming messages growing the array never retrigger the jump.
  const hasMessages = messages.length > 0;
  useEffect(() => {
    if (!hasMessages || !jumpMsg) return;
    let tries = 0;
    const clearParam = () => router.replace(pathname, { scroll: false });
    const iv = setInterval(() => {
      const el = document.getElementById(`msg-${jumpMsg}`);
      if (el) {
        clearInterval(iv);
        el.scrollIntoView({ behavior: "smooth", block: "center" });
        // flash via a WAAPI animation on an inner wrapper (survives row re-renders)
        const flash = el.querySelector<HTMLElement>("[data-mid-body]");
        const target = flash ?? el;
        target.animate(
          [{ backgroundColor: "rgba(253,169,19,0.22)" }, { backgroundColor: "rgba(253,169,19,0.22)", offset: 0.7 }, { backgroundColor: "rgba(253,169,19,0)" }],
          { duration: 2400, easing: "ease" },
        );
        clearParam();
      } else if (++tries > 30) {
        clearInterval(iv);
        clearParam(); // target isn't in this view — stop retrying on every new message
      }
    }, 80);
    return () => clearInterval(iv);
  }, [hasMessages, channelId, jumpMsg, router, pathname]);

  const isDm = channel?.kind === "dm" || channel?.kind === "group_dm";
  const other =
    channel?.kind === "dm" ? userById(channel.memberIds.find((id) => id !== me?.id) ?? "") : undefined;
  const title = channel ? channel.name : "…";

  const grouped = useMemo(() => groupByDay(messages), [messages]);

  if (!channel) {
    return (
      <div className="flex flex-1 items-center justify-center text-muted">Select a conversation</div>
    );
  }

  return (
    <div className="flex min-w-0 flex-1 flex-col">
      <Header
        eyebrow={isDm ? "Direct message" : channel.external ? "External · Slack Connect" : channel.kind === "private" ? "Private channel" : "Channel"}
        title={
          <span className="flex items-center gap-1.5">
            {channel.kind === "channel" && <Hash size={16} className="text-dim" />}
            {channel.kind === "private" && <Lock size={14} className="text-dim" />}
            {isDm && other && <Avatar user={other} size={20} rounded="6px" />}
            {title}
          </span>
        }
        subtitle={channel.topic ?? (other ? `${other.title} · ${other.presence}` : undefined)}
      >
        {!isDm && (
          <button
            onClick={() => openModal("channel-members", channelId)}
            title="View members"
            className="mr-1 hidden items-center gap-1.5 rounded-control border border-border bg-panel px-2 py-1.5 text-[12px] text-muted transition-colors hover:bg-hover hover:text-ink xl:flex"
          >
            <Users size={14} />
            <span className="flex -space-x-1.5">
              {channel.memberIds.slice(0, 4).map((id) => {
                const u = userById(id);
                return u ? <Avatar key={id} user={u} size={20} showPresence={false} rounded="999px" /> : null;
              })}
            </span>
            <span className="font-semibold text-ink">{channel.memberIds.length}</span>
          </button>
        )}
        {!isDm && (
          <IconButton label="Add people" onClick={() => openModal("channel-members", channelId)}>
            <UserPlus size={18} />
          </IconButton>
        )}
        {flags.huddles && (
          <IconButton label="Start huddle" onClick={() => startHuddle(channelId)}>
            <Video size={18} />
          </IconButton>
        )}
        {flags.huddles && (
          <IconButton label="Start a call" onClick={() => startHuddle(channelId)} className="hidden sm:grid">
            <Phone size={18} />
          </IconButton>
        )}
        {flags.translation && (
          <div className="relative">
            <IconButton label="Translate conversation" onClick={() => setLangOpen((o) => !o)} className={chatLang ? "text-accent" : undefined}>
              <Languages size={18} />
            </IconButton>
            {langOpen && (
              <LanguageMenu
                align="right"
                onPick={(lang) => { setChatLang(lang); setLangOpen(false); }}
                onClose={() => setLangOpen(false)}
              />
            )}
          </div>
        )}
        <IconButton label="Channel details" onClick={() => openModal("channel-details", channelId)}>
          <Info size={18} />
        </IconButton>
      </Header>

      {/* whole-conversation translation banner */}
      {chatLang && (
        <div className="flex shrink-0 items-center gap-2 border-b border-border bg-info/5 px-4 py-1.5 text-[12.5px]">
          <Languages size={13} className="shrink-0 text-info" />
          <span className="text-muted">Conversation translated to <b className="text-ink">{chatLang}</b></span>
          <button onClick={() => setChatLang(null)} className="ml-auto font-semibold text-info hover:underline">Show originals</button>
        </div>
      )}

      {/* pinned bar */}
      {flags.pins && pinned.length > 0 && (
        <div className="flex shrink-0 items-center gap-2 border-b border-border bg-accent-soft/40 px-4 py-1.5 text-[12.5px]">
          <Pin size={13} className="shrink-0 text-accent" />
          <span className="shrink-0 text-muted">{pinned.length} pinned</span>
          <span className="min-w-0 flex-1 truncate text-ink/80">· {pinned[0].body}</span>
        </div>
      )}

      {/* messages */}
      <div ref={scrollRef} className="min-h-0 flex-1 overflow-y-auto">
        <ChannelIntro channelName={title} isDm={!!isDm} />
        {grouped.map((g) => (
          <div key={g.day}>
            <DayDivider label={g.day} />
            {g.items.map((m, i) => {
              const prev = g.items[i - 1];
              const groupedMsg =
                !!prev && prev.authorId === m.authorId && m.ts - prev.ts < 5 * 60_000 && !m.systemEvent;
              return (
                <MessageItem
                  key={m.id}
                  message={m}
                  grouped={groupedMsg}
                  autoTranslateTo={chatLang}
                  onReact={(e) => react(m.id, e)}
                  onOpenThread={() => openThread(channelId, m.threadRootId ?? m.id)}
                  onPin={() => pin(m.id)}
                  onSave={() => save(m.id)}
                  onEdit={(body) => edit(m.id, body)}
                  onDelete={() => remove(m.id)}
                />
              );
            })}
          </div>
        ))}
        <div ref={bottomRef} className="h-4" />
      </div>

      {/* typing indicator — fixed just above the composer */}
      <div className="h-6 shrink-0">
        {flags.typingIndicator && typing.length > 0 && <TypingIndicator userIds={typing} userById={userById} />}
      </div>

      <Composer
        placeholder={`Message ${channel.kind === "channel" ? "#" + title : title}`}
        onSend={async (body, opts) => { await send(body, opts); }}
        onStartHuddle={() => startHuddle(channelId)}
      />
    </div>
  );
}

function DayDivider({ label }: { label: string }) {
  return (
    <div className="sticky top-0 z-10 my-2 flex items-center gap-3 px-4">
      <div className="h-px flex-1 bg-border" />
      <span className="rounded-pill border border-border bg-surface px-3 py-0.5 text-[11px] font-semibold text-muted">
        {label}
      </span>
      <div className="h-px flex-1 bg-border" />
    </div>
  );
}

function TypingIndicator({
  userIds,
  userById,
}: {
  userIds: string[];
  userById: (id: string) => { name: string; avatarColor?: string } | undefined;
}) {
  const typers = userIds.map((id) => userById(id)).filter(Boolean) as { name: string; avatarColor?: string }[];
  if (!typers.length) return null;
  return (
    // left inset matches the editor's text (composer px-4 + editor px-3.5) so the
    // loader + text line up under the editor rather than hugging the sidebar edge.
    <div className="flex items-center gap-2 py-1 pl-[30px] pr-4 text-[12px] text-muted">
      <span className="flex items-center gap-0.5">
        <span className="h-1.5 w-1.5 animate-blink rounded-full bg-accent" style={{ animationDelay: "0ms" }} />
        <span className="h-1.5 w-1.5 animate-blink rounded-full bg-accent" style={{ animationDelay: "160ms" }} />
        <span className="h-1.5 w-1.5 animate-blink rounded-full bg-accent" style={{ animationDelay: "320ms" }} />
      </span>
      <span className="truncate">
        {typers.map((u, i) => (
          <span key={i}>
            <span className="font-semibold" style={{ color: u.avatarColor ?? "var(--c-accent)" }}>{u.name}</span>
            {i < typers.length - 1 ? ", " : ""}
          </span>
        ))}
        {" "}
        {typers.length === 1 ? "is" : "are"} typing…
      </span>
    </div>
  );
}

function ChannelIntro({ channelName, isDm }: { channelName: string; isDm: boolean }) {
  return (
    <div className="px-4 pb-1 pt-6">
      <div className="brand-mark mb-3 grid h-12 w-12 place-items-center rounded-card text-lg font-black text-black/80">
        {channelName.slice(0, 1).toUpperCase()}
      </div>
      <h2 className="text-[20px] font-black">
        {isDm ? channelName : `#${channelName}`}
      </h2>
      <p className="mt-1 max-w-lg text-[13.5px] text-muted">
        {isDm
          ? `This is the beginning of your direct message history with ${channelName}.`
          : `This is the very beginning of the #${channelName} channel. Say hello 👋`}
      </p>
    </div>
  );
}
