"use client";

import Link from "next/link";
import { Suspense, useCallback, useEffect, useMemo, useState } from "react";
import type { Message } from "@slackwsh/contracts";
import { AppShell, ScreenHeader, useWorkspaceIdParam } from "../../components/AppShell";
import { UserAvatar } from "../../components/UserAvatar";
import { api, ApiError } from "../../lib/api";
import { setChannelUnread } from "../../lib/unread-store";

interface UnreadHit {
  message: Message;
  channelName: string | null;
  channelType: string;
  authorName: string;
}

function errorText(error: unknown) {
  if (error instanceof ApiError && error.body && typeof error.body === "object") {
    const message = (error.body as { message?: unknown }).message;
    if (typeof message === "string") return message;
  }
  return error instanceof Error ? error.message : "Could not update unreads.";
}

function roomLabel(hit: UnreadHit) {
  if (hit.channelType === "dm") return "Direct message";
  if (hit.channelType === "group_dm") return "Group message";
  return hit.channelName ? `#${hit.channelName}` : "Channel";
}

function UnreadsView() {
  const workspaceId = useWorkspaceIdParam();
  const [results, setResults] = useState<UnreadHit[]>([]);
  const [loading, setLoading] = useState(true);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const refresh = useCallback(() => {
    if (!workspaceId) return;
    setLoading(true);
    void api.unreads(workspaceId)
      .then(({ results: next }) => {
        setResults(next);
        setError(null);
      })
      .catch((err) => setError(errorText(err)))
      .finally(() => setLoading(false));
  }, [workspaceId]);

  useEffect(() => {
    refresh();
    window.addEventListener("slackwsh:message", refresh);
    window.addEventListener("slackwsh:unread", refresh);
    return () => {
      window.removeEventListener("slackwsh:message", refresh);
      window.removeEventListener("slackwsh:unread", refresh);
    };
  }, [refresh]);

  const channelCount = useMemo(
    () => new Set(results.map((hit) => String(hit.message.channelId))).size,
    [results],
  );

  async function markRead(hit: UnreadHit) {
    if (!workspaceId || busy) return;
    setBusy(true);
    setError(null);
    try {
      await api.markChannelRead(workspaceId, String(hit.message.channelId), hit.message.seq);
      const next = results.filter(
        (row) => String(row.message.channelId) !== String(hit.message.channelId) || row.message.seq > hit.message.seq,
      );
      setResults(next);
      setChannelUnread(
        workspaceId,
        hit.message.channelId,
        next.filter((row) => String(row.message.channelId) === String(hit.message.channelId)).length,
      );
    } catch (err) {
      setError(errorText(err));
    } finally {
      setBusy(false);
    }
  }

  async function markAllRead() {
    if (!workspaceId || busy || results.length === 0) return;
    setBusy(true);
    setError(null);
    const latestByChannel = new Map<string, number>();
    for (const hit of results) {
      const channelId = String(hit.message.channelId);
      latestByChannel.set(channelId, Math.max(latestByChannel.get(channelId) ?? 0, hit.message.seq));
    }
    try {
      await Promise.all(
        [...latestByChannel].map(([channelId, seq]) => api.markChannelRead(workspaceId, channelId, seq)),
      );
      setResults([]);
      for (const channelId of latestByChannel.keys()) setChannelUnread(workspaceId, channelId, 0);
    } catch (err) {
      setError(errorText(err));
      refresh();
    } finally {
      setBusy(false);
    }
  }

  return (
    <AppShell active="messages" title="Unreads" subtitle="Messages you have not read yet">
      <ScreenHeader
        title="Unreads"
        subtitle={`${results.length} unread ${results.length === 1 ? "message" : "messages"} in ${channelCount} ${channelCount === 1 ? "conversation" : "conversations"}`}
        actions={
          <button className="screen-btn" type="button" disabled={busy || results.length === 0} onClick={() => void markAllRead()}>
            {busy ? "Updating..." : "Mark all as read"}
          </button>
        }
      />
      <div className="screen-body">
        {error && <p className="error-text">{error}</p>}
        <div className="screen-list tall">
          {results.map((hit) => (
            <article className="screen-thread" key={hit.message.id}>
              <UserAvatar userId={hit.message.authorId} name={hit.authorName} className="activity-avatar" />
              <div className="screen-thread-main">
                <span className="screen-thread-meta">
                  {hit.authorName} · {roomLabel(hit)} · {new Date(hit.message.createdAt).toLocaleString()}
                </span>
                <span className="screen-thread-text">{hit.message.text || "(attachment)"}</span>
                <div className="saved-actions">
                  <Link
                    className="screen-btn"
                    href={`/channel?workspaceId=${workspaceId}&channelId=${hit.message.channelId}#msg-${hit.message.id}`}
                  >
                    Open
                  </Link>
                  <button className="screen-btn" type="button" disabled={busy} onClick={() => void markRead(hit)}>
                    Mark as read
                  </button>
                </div>
              </div>
            </article>
          ))}
          {loading && <div className="empty-state">Loading unread messages...</div>}
          {!loading && results.length === 0 && <div className="empty-state">You&apos;re all caught up.</div>}
        </div>
      </div>
    </AppShell>
  );
}

export default function UnreadsPage() {
  return (
    <Suspense fallback={<main className="page"><div className="empty-state">Loading unreads...</div></main>}>
      <UnreadsView />
    </Suspense>
  );
}
