"use client";

import { useState, useEffect, useCallback } from "react";
import Link from "next/link";
import { ArrowLeft, Heart, Send } from "lucide-react";
import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
import remarkGfm from "remark-gfm";
import { api } from "@/lib/api";
import { formatDate } from "@/lib/utils";
import {
  hasNostrExtension,
  getPublicKey,
  signEvent,
  publishEvent,
  shortenPubkey,
  fetchNostrProfile,
  fetchLongformFromRelays,
  fetchEventFromRelays,
  resolveEventFromRelays,
  type NostrProfile,
} from "@/lib/nostr";
import { Navbar } from "@/components/public/Navbar";
import { Footer } from "@/components/public/Footer";
import { markdownComponents } from "./markdownComponents";
import { remarkNostr } from "./remarkNostr";

interface Post {
  id: string;
  slug: string;
  title: string;
  content: string;
  excerpt?: string;
  authorName?: string;
  authorPubkey?: string;
  publishedAt?: string;
  createdAt?: string;
  nostrEventId?: string;
  naddr?: string;
  categories?: { category: { id: string; name: string; slug: string } }[];
}

interface NostrReply {
  id: string;
  pubkey: string;
  content: string;
  created_at: number;
}

// Builds a renderable Post from a raw long-form Nostr event, used when a slug is
// a NIP-19 reference (naddr/nevent/note) that was never indexed by the backend.
function postFromEvent(event: any, slug: string): Post {
  const tag = (name: string): string | undefined =>
    event.tags?.find((t: string[]) => t[0] === name)?.[1];
  const publishedAtSec = Number(tag("published_at")) || event.created_at;

  return {
    id: event.id,
    slug,
    title: tag("title") || "Untitled",
    content: event.content || "",
    excerpt: tag("summary"),
    authorPubkey: event.pubkey,
    publishedAt: new Date(publishedAtSec * 1000).toISOString(),
    nostrEventId: event.id,
    naddr: slug.startsWith("naddr") ? slug : undefined,
    categories: (event.tags || [])
      .filter((t: string[]) => t[0] === "t" && t[1])
      .map((t: string[]) => ({ category: { id: t[1], name: t[1], slug: t[1] } })),
  };
}

function ArticleSkeleton() {
  const widths = [85, 92, 78, 95, 88, 72, 90, 83];
  return (
    <div className="animate-pulse max-w-3xl mx-auto">
      <div className="flex gap-2 mb-6">
        <div className="h-5 w-20 bg-surface-container-high rounded-full" />
        <div className="h-5 w-16 bg-surface-container-high rounded-full" />
      </div>
      <div className="h-12 w-3/4 bg-surface-container-high rounded mb-4" />
      <div className="h-12 w-1/2 bg-surface-container-high rounded mb-8" />
      <div className="h-5 w-48 bg-surface-container-high rounded mb-16" />
      <div className="space-y-4">
        {widths.map((w, i) => (
          <div
            key={i}
            className="h-4 bg-surface-container-high rounded"
            style={{ width: `${w}%` }}
          />
        ))}
      </div>
    </div>
  );
}

function RelayLoading() {
  return (
    <div className="flex items-center justify-center gap-3 py-16 text-on-surface-variant">
      <span
        className="inline-block w-5 h-5 rounded-full border-2 border-primary/30 border-t-primary animate-spin"
        aria-hidden
      />
      <span className="text-sm font-medium">Fetching from Nostr relays…</span>
    </div>
  );
}

export default function BlogPostClient({ slug }: { slug: string }) {
  const [post, setPost] = useState<Post | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [liked, setLiked] = useState(false);
  const [likeCount, setLikeCount] = useState(0);
  const [comment, setComment] = useState("");
  const [replies, setReplies] = useState<NostrReply[]>([]);
  const [hasNostr, setHasNostr] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [authorProfile, setAuthorProfile] = useState<NostrProfile | null>(null);
  const [liveContent, setLiveContent] = useState<string | null>(null);
  const [loadingContent, setLoadingContent] = useState(false);
  const [resolvingFromRelays, setResolvingFromRelays] = useState(false);
  const [contentError, setContentError] = useState(false);
  const [retryKey, setRetryKey] = useState(0);

  const retry = useCallback(() => setRetryKey((k) => k + 1), []);

  useEffect(() => {
    setHasNostr(hasNostrExtension());
  }, []);

  useEffect(() => {
    if (!slug) return;
    let cancelled = false;
    setLoading(true);
    setError(null);
    setLiveContent(null);
    setPost(null);
    setAuthorProfile(null);
    setContentError(false);
    setResolvingFromRelays(false);

    // Loads the author's profile, then hydrates the article body from relays
    // when only metadata is present (indexed posts store an empty body).
    const hydrate = (data: Post) => {
      if (cancelled) return;
      setPost(data);
      setLoading(false);
      if (data?.authorPubkey) {
        fetchNostrProfile(data.authorPubkey)
          .then((profile) => !cancelled && setAuthorProfile(profile))
          .catch(() => {});
      }
      if (!data?.content && (data?.naddr || data?.nostrEventId)) {
        setLoadingContent(true);
        setContentError(false);
        const fetchPromise = data.naddr
          ? fetchLongformFromRelays(data.naddr)
          : fetchEventFromRelays(data.nostrEventId!);
        fetchPromise
          .then((event) => {
            if (cancelled) return;
            if (event?.content) setLiveContent(event.content);
            else setContentError(true); // not found / timed out
          })
          .catch(() => !cancelled && setContentError(true))
          .finally(() => !cancelled && setLoadingContent(false));
      }
    };

    api
      .getPost(slug)
      .then((data) => hydrate(data))
      .catch(() => {
        // Not indexed: treat the slug itself as a NIP-19 reference and resolve
        // the long-form note live from relays.
        if (cancelled) return;
        setLoading(false);
        setResolvingFromRelays(true);
        resolveEventFromRelays(slug)
          .then((event) => {
            if (cancelled) return;
            setResolvingFromRelays(false);
            if (event) hydrate(postFromEvent(event, slug));
            else setError("Post not found");
          })
          .catch((err) => {
            if (cancelled) return;
            setResolvingFromRelays(false);
            setError(err?.message || "Post not found");
          });
      });

    return () => {
      cancelled = true;
    };
  }, [slug, retryKey]);

  useEffect(() => {
    if (!slug) return;
    api.getPostReactions(slug)
      .then((data) => setLikeCount(data.count))
      .catch(() => {});

    api.getPostReplies(slug)
      .then((data) => setReplies(data.replies || []))
      .catch(() => {});
  }, [slug]);

  const handleLike = useCallback(async () => {
    if (liked || !post?.nostrEventId || !hasNostr) return;
    try {
      const pubkey = await getPublicKey();
      const reactionEvent = {
        kind: 7,
        created_at: Math.floor(Date.now() / 1000),
        tags: [["e", post.nostrEventId], ["p", post.authorPubkey || ""]],
        content: "+",
        pubkey,
      };
      const signedReaction = await signEvent(reactionEvent);
      await publishEvent(signedReaction);
      setLiked(true);
      setLikeCount((c) => c + 1);
    } catch {
      // User rejected or extension unavailable
    }
  }, [liked, post, hasNostr]);

  const handleComment = useCallback(async () => {
    if (!comment.trim() || !post?.nostrEventId || !hasNostr) return;
    setSubmitting(true);
    try {
      const pubkey = await getPublicKey();
      const replyEvent = {
        kind: 1,
        created_at: Math.floor(Date.now() / 1000),
        tags: [["e", post.nostrEventId, "", "reply"], ["p", post.authorPubkey || ""]],
        content: comment.trim(),
        pubkey,
      };
      const signed = await signEvent(replyEvent);
      await publishEvent(signed);
      setReplies((prev) => [
        ...prev,
        {
          id: signed.id || Date.now().toString(),
          pubkey,
          content: comment.trim(),
          created_at: Math.floor(Date.now() / 1000),
        },
      ]);
      setComment("");
    } catch {
      // User rejected or extension unavailable
    } finally {
      setSubmitting(false);
    }
  }, [comment, post, hasNostr]);

  const categories = post?.categories?.map((c) => c.category) || [];

  return (
    <>
      <Navbar />

      <div className="min-h-screen">
        <div className="max-w-3xl mx-auto px-8 pt-12 pb-24">
          <Link
            href="/blog"
            className="inline-flex items-center gap-2 text-on-surface-variant hover:text-primary transition-colors mb-12 text-sm font-medium"
          >
            <ArrowLeft size={16} />
            Back to Blog
          </Link>

          {loading && <ArticleSkeleton />}

          {resolvingFromRelays && !post && <RelayLoading />}

          {error && (
            <div className="bg-error-container/20 text-error rounded-xl p-6">
              <p className="mb-4">Failed to load post: {error}</p>
              <button
                onClick={retry}
                className="px-4 py-2 rounded-lg bg-surface-container-high text-on-surface hover:bg-surface-bright transition-colors text-sm font-semibold"
              >
                Try again
              </button>
            </div>
          )}

          {!loading && !error && post && (
            <>
              <header className="mb-16">
                {categories.length > 0 && (
                  <div className="flex flex-wrap gap-2 mb-6">
                    {categories.map((cat) => (
                      <span
                        key={cat.id}
                        className="px-3 py-1 text-xs font-bold uppercase tracking-widest text-primary bg-primary/10 rounded-full"
                      >
                        {cat.name}
                      </span>
                    ))}
                  </div>
                )}

                <h1 className="text-4xl md:text-5xl font-black tracking-tight leading-tight mb-6">
                  {post.title}
                </h1>

                <div className="flex items-center gap-3 text-sm text-on-surface-variant/60">
                  {(authorProfile || post.authorName || post.authorPubkey) && (
                    <div className="flex items-center gap-2.5">
                      {authorProfile?.picture && (
                        <img
                          src={authorProfile.picture}
                          alt={authorProfile.name || post.authorName || "Author"}
                          className="w-8 h-8 rounded-full object-cover bg-zinc-800 shrink-0"
                          onError={(e) => { (e.target as HTMLImageElement).style.display = "none"; }}
                        />
                      )}
                      <span className="font-medium text-on-surface-variant">
                        {authorProfile?.name || post.authorName || shortenPubkey(post.authorPubkey!)}
                      </span>
                    </div>
                  )}
                  {(post.publishedAt || post.createdAt) && (
                    <>
                      {(authorProfile || post.authorName || post.authorPubkey) && (
                        <span className="text-on-surface-variant/30">·</span>
                      )}
                      <span>
                        {formatDate(post.publishedAt || post.createdAt!)}
                      </span>
                    </>
                  )}
                </div>
              </header>

              <article className="mb-16">
                {loadingContent ? (
                  <RelayLoading />
                ) : contentError && !(post.content || liveContent) ? (
                  <div className="text-center py-12">
                    <p className="text-on-surface-variant mb-4">
                      Couldn&apos;t fetch this article from the Nostr relays. It may be
                      temporarily unavailable.
                    </p>
                    <button
                      onClick={retry}
                      className="px-4 py-2 rounded-lg bg-surface-container-high text-on-surface hover:bg-surface-bright transition-colors text-sm font-semibold"
                    >
                      Try again
                    </button>
                  </div>
                ) : (
                  <ReactMarkdown
                    remarkPlugins={[remarkGfm, remarkNostr]}
                    components={markdownComponents}
                    // Preserve nostr: links (react-markdown strips unknown
                    // schemes by default), so mentions/notes resolve correctly.
                    urlTransform={(url) =>
                      url.startsWith("nostr:") ? url : defaultUrlTransform(url)
                    }
                  >
                    {post.content || liveContent || ""}
                  </ReactMarkdown>
                )}
              </article>

              <section className="bg-surface-container-low rounded-xl p-8 mb-16">
                <div className="flex items-center gap-6 mb-8">
                  <button
                    onClick={handleLike}
                    disabled={!hasNostr}
                    title={hasNostr ? "Like this post" : "Install a Nostr extension to interact"}
                    className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-colors ${
                      liked
                        ? "bg-primary/20 text-primary"
                        : hasNostr
                          ? "bg-surface-container-high text-on-surface hover:bg-surface-bright"
                          : "bg-surface-container-high text-on-surface/40 cursor-not-allowed"
                    }`}
                  >
                    <Heart size={18} fill={liked ? "currentColor" : "none"} />
                    <span className="font-semibold">{likeCount}</span>
                  </button>
                  {!hasNostr && (
                    <span className="text-on-surface-variant/50 text-xs">
                      Install a Nostr extension to like and comment
                    </span>
                  )}
                </div>

                <h3 className="text-lg font-bold mb-6">
                  Comments {replies.length > 0 && `(${replies.length})`}
                </h3>

                {hasNostr && (
                  <div className="flex gap-3 mb-8">
                    <textarea
                      value={comment}
                      onChange={(e) => setComment(e.target.value)}
                      placeholder="Share your thoughts..."
                      rows={3}
                      className="flex-1 bg-surface-container-highest text-on-surface rounded-lg p-4 resize-none placeholder:text-on-surface-variant/40 focus:outline-none focus:ring-1 focus:ring-primary/40"
                    />
                    <button
                      onClick={handleComment}
                      disabled={!comment.trim() || submitting}
                      className="self-end px-4 py-3 bg-primary text-on-primary rounded-lg font-semibold hover:scale-105 transition-transform disabled:opacity-30 disabled:cursor-not-allowed"
                    >
                      <Send size={18} />
                    </button>
                  </div>
                )}

                {replies.length > 0 ? (
                  <div className="space-y-6">
                    {replies.map((r) => (
                      <div
                        key={r.id}
                        className="bg-surface-container-high rounded-lg p-4"
                      >
                        <div className="flex items-center gap-2.5 mb-2">
                          <span className="font-semibold text-xs font-mono text-on-surface-variant/70">
                            {shortenPubkey(r.pubkey)}
                          </span>
                          <span className="text-on-surface-variant/30">·</span>
                          <span className="text-xs text-on-surface-variant/50">
                            {formatDate(new Date(r.created_at * 1000))}
                          </span>
                        </div>
                        <p className="text-on-surface-variant text-sm leading-relaxed">
                          {r.content}
                        </p>
                      </div>
                    ))}
                  </div>
                ) : (
                  <p className="text-on-surface-variant/50 text-sm">
                    No comments yet. Be the first to share your thoughts.
                  </p>
                )}
              </section>
            </>
          )}
        </div>
      </div>

      <Footer />
    </>
  );
}
