"use client";

import { useEffect, useMemo, useState } from "react";
import { KPIStrip } from "@/components/prism/KPIStrip";
import { TickerHeader } from "@/components/prism/TickerHeader";
import { api } from "@/lib/api";
import { formatNewsTimestamp } from "@/lib/format";
import { buildKpis } from "@/lib/overview";
import type { NewsAggregate, NewsItem, NewsResponse, TickerOverview } from "@/types/api";

type NewsState = "loading" | "ready" | "error" | "empty";
type RangeFilter = "all" | "24h" | "7d" | "30d";
type SentimentFilter = "all" | "bullish" | "neutral" | "bearish";

const SOURCE_TOP_N = 6;

function buildRangeCutoffs(nowMs: number): Record<RangeFilter, string | null> {
  return {
    all: null,
    "24h": new Date(nowMs - 24 * 60 * 60 * 1000).toISOString(),
    "7d": new Date(nowMs - 7 * 24 * 60 * 60 * 1000).toISOString(),
    "30d": new Date(nowMs - 30 * 24 * 60 * 60 * 1000).toISOString(),
  };
}

const SENTIMENT_LABELS: Record<SentimentFilter, string> = {
  all: "All",
  bullish: "Bull",
  neutral: "Neutral",
  bearish: "Bear",
};

const RANGE_LABELS: Record<RangeFilter, string> = {
  all: "All",
  "24h": "24H",
  "7d": "7D",
  "30d": "30D",
};

type Props = {
  ticker: string;
  overview: TickerOverview;
  isSaved: boolean;
  onToggleWatchlist: () => void;
};

function SkeletonBlock({ height, className = "", style }: { height: number; className?: string; style?: React.CSSProperties }) {
  return (
    <div
      className={className}
      style={{
        ...style,
        height,
        borderRadius: "var(--r-3)",
        background: "var(--ink-2, #181d26)",
        animation: "psm-pulse 1.4s ease-in-out infinite",
      }}
    />
  );
}

function NewsSkeleton() {
  return (
    <div data-testid="news-loading">
      <SkeletonBlock height={120} style={{ marginBottom: "var(--sp-4)" }} />
      <SkeletonBlock height={48} style={{ marginBottom: "var(--sp-4)" }} />
      <div style={{ display: "flex", flexDirection: "column", gap: "var(--sp-3)" }}>
        {[0, 1, 2].map((i) => (
          <SkeletonBlock key={i} height={110} />
        ))}
      </div>
    </div>
  );
}

function LedeCard({
  ticker,
  itemCount,
  provider,
  aggregate,
}: {
  ticker: string;
  itemCount: number;
  provider: NewsResponse["provider"];
  aggregate: NewsAggregate | null;
}) {
  const providerLabel = provider ?? "aggregated";

  return (
    <div
      data-testid="news-lede"
      className="psm-card"
      style={{
        display: "grid",
        gridTemplateColumns: aggregate ? "1fr minmax(220px, auto)" : "1fr",
        gap: "var(--sp-5)",
        alignItems: "stretch",
        marginBottom: "var(--sp-4)",
      }}
    >
      <div style={{ display: "flex", flexDirection: "column", gap: "var(--sp-2)", justifyContent: "center" }}>
        <div
          style={{
            fontFamily: "var(--font-sans)",
            fontSize: "var(--fs-12)",
            fontWeight: "var(--w-semibold)",
            textTransform: "uppercase",
            letterSpacing: "var(--tr-wider)",
            color: "var(--fg-3)",
          }}
        >
          Market Tape
        </div>
        <div
          style={{
            fontFamily: "var(--font-display)",
            fontSize: "var(--fs-30)",
            fontWeight: "var(--w-medium)",
            letterSpacing: "var(--tr-tight)",
            lineHeight: "var(--lh-snug)",
            color: "var(--fg-1)",
          }}
        >
          Headlines for {ticker}
        </div>
        <div
          style={{
            fontFamily: "var(--font-mono)",
            fontSize: "var(--fs-13)",
            fontVariantNumeric: "tabular-nums",
            color: "var(--fg-3)",
          }}
        >
          30 days · {itemCount} {itemCount === 1 ? "article" : "articles"} · {providerLabel}
        </div>
      </div>

      {aggregate && (
        <div
          data-testid="news-lede-aggregate"
          style={{
            display: "flex",
            flexDirection: "column",
            gap: "var(--sp-3)",
            justifyContent: "center",
            alignItems: "flex-end",
          }}
        >
          <div
            style={{
              fontFamily: "var(--font-mono)",
              fontSize: "var(--fs-13)",
              fontVariantNumeric: "tabular-nums",
              color: "var(--fg-2)",
            }}
          >
            Last 7d buzz: {aggregate.buzz_articles_last_week}
          </div>
          <div style={{ display: "flex", gap: "var(--sp-2)" }}>
            <SentimentChip sentiment="bullish" label={`${Math.round(aggregate.bullish_pct * 100)}% Bull`} />
            <SentimentChip sentiment="neutral" label={`${Math.round(aggregate.neutral_pct * 100)}% Neutral`} />
            <SentimentChip sentiment="bearish" label={`${Math.round(aggregate.bearish_pct * 100)}% Bear`} />
          </div>
        </div>
      )}
    </div>
  );
}

function SentimentChip({ sentiment, label }: { sentiment: SentimentFilter; label: string }) {
  const colors: Record<SentimentFilter, { bg: string; color: string; border: string }> = {
    bullish: { bg: "var(--positive-bg, #15241A)", color: "var(--positive, #4F8C5E)", border: "1px solid rgba(79, 140, 94, 0.3)" },
    bearish: { bg: "var(--negative-bg, #2A1517)", color: "var(--negative, #B5494B)", border: "1px solid rgba(181, 73, 75, 0.3)" },
    neutral: { bg: "var(--ink-2, #181d26)", color: "var(--fg-3, #8E8676)", border: "1px solid var(--line-2, #2E3645)" },
    all: { bg: "var(--ink-2, #181d26)", color: "var(--fg-3, #8E8676)", border: "1px solid var(--line-2, #2E3645)" },
  };

  return (
    <span
      style={{
        display: "inline-flex",
        alignItems: "center",
        justifyContent: "center",
        padding: "2px 10px",
        borderRadius: "999px",
        fontFamily: "var(--font-mono)",
        fontSize: "var(--fs-12)",
        fontVariantNumeric: "tabular-nums",
        fontWeight: "var(--w-medium)",
        background: colors[sentiment].bg,
        color: colors[sentiment].color,
        border: colors[sentiment].border,
        whiteSpace: "nowrap",
      }}
    >
      {label}
    </span>
  );
}

function FilterChip({
  label,
  active,
  onClick,
  pressed,
}: {
  label: string;
  active: boolean;
  onClick: () => void;
  pressed?: boolean;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      aria-pressed={pressed ?? active}
      style={{
        padding: "5px 14px",
        borderRadius: "999px",
        border: active ? "1px solid var(--brass, #C2AA7A)" : "1px solid var(--line-2, #2E3645)",
        background: active ? "var(--brass, #C2AA7A)" : "var(--ink-2, #181d26)",
        color: active ? "var(--brass-ink, #17120A)" : "var(--fg-3, #8E8676)",
        fontFamily: "var(--font-sans)",
        fontSize: "var(--fs-12)",
        fontWeight: "var(--w-medium)",
        letterSpacing: "var(--tr-wide)",
        cursor: "pointer",
        transition: "background 150ms ease, color 150ms ease, border-color 150ms ease",
        whiteSpace: "nowrap",
      }}
    >
      {label}
    </button>
  );
}

function FilterBar({
  items,
  range,
  setRange,
  source,
  setSource,
  sentiment,
  setSentiment,
  rangeCutoff,
}: {
  items: NewsItem[];
  range: RangeFilter;
  setRange: (r: RangeFilter) => void;
  source: string;
  setSource: (s: string) => void;
  sentiment: SentimentFilter;
  setSentiment: (s: SentimentFilter) => void;
  rangeCutoff: Record<RangeFilter, string | null>;
}) {
  const [dropdownOpen, setDropdownOpen] = useState(false);

  const sourceCounts = useMemo(() => {
    const counts = new Map<string, number>();
    items.forEach((item) => {
      counts.set(item.source, (counts.get(item.source) ?? 0) + 1);
    });
    return Array.from(counts.entries()).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
  }, [items]);

  const topSources = sourceCounts.slice(0, SOURCE_TOP_N);
  const tailSources = sourceCounts.slice(SOURCE_TOP_N);
  const isTailSourceSelected = tailSources.some(([s]) => s === source);

  const filtered = items.filter((item) => {
    const cutoff = rangeCutoff[range];
    if (cutoff != null && item.published_at < cutoff) return false;
    if (source !== "all" && item.source !== source) return false;
    if (sentiment !== "all" && item.sentiment !== sentiment) return false;
    return true;
  });

  const sentimentCounts = {
    bullish: filtered.filter((i) => i.sentiment === "bullish").length,
    neutral: filtered.filter((i) => i.sentiment === "neutral").length,
    bearish: filtered.filter((i) => i.sentiment === "bearish").length,
  };

  const distinctSources = new Set(filtered.map((i) => i.source)).size;

  const ranges: RangeFilter[] = ["all", "24h", "7d", "30d"];
  const sentiments: SentimentFilter[] = ["all", "bullish", "neutral", "bearish"];

  return (
    <div
      className="psm-card"
      style={{
        display: "flex",
        flexDirection: "column",
        gap: "var(--sp-3)",
        marginBottom: "var(--sp-4)",
        padding: "var(--sp-3) var(--sp-4)",
      }}
    >
      <div style={{ display: "flex", flexWrap: "wrap", gap: "var(--sp-4)", alignItems: "center" }}>
        <div data-testid="news-filter-range" role="group" aria-label="Date range" style={{ display: "flex", gap: "var(--sp-2)", flexWrap: "wrap", alignItems: "center" }}>
          <span
            style={{
              fontFamily: "var(--font-mono)",
              fontSize: "var(--fs-11)",
              textTransform: "uppercase",
              letterSpacing: "var(--tr-wider)",
              color: "var(--fg-4, #5E5849)",
            }}
          >
            Range
          </span>
          {ranges.map((r) => (
            <FilterChip key={r} label={RANGE_LABELS[r]} active={range === r} onClick={() => setRange(r)} />
          ))}
        </div>

        <div data-testid="news-filter-source" role="group" aria-label="Source" style={{ display: "flex", gap: "var(--sp-2)", flexWrap: "wrap", alignItems: "center" }}>
          <span
            style={{
              fontFamily: "var(--font-mono)",
              fontSize: "var(--fs-11)",
              textTransform: "uppercase",
              letterSpacing: "var(--tr-wider)",
              color: "var(--fg-4, #5E5849)",
            }}
          >
            Source
          </span>
          <FilterChip label="All" active={source === "all" && !isTailSourceSelected} onClick={() => setSource("all")} />
          {topSources.map(([src, count]) => (
            <FilterChip
              key={src}
              label={`${src} (${count})`}
              active={source === src}
              onClick={() => setSource(src)}
            />
          ))}
          {tailSources.length > 0 && (
            <div style={{ position: "relative" }}>
              <button
                type="button"
                data-testid="news-filter-source-more"
                aria-expanded={dropdownOpen}
                aria-controls="news-source-dropdown"
                onClick={() => setDropdownOpen((open) => !open)}
                style={{
                  padding: "5px 14px",
                  borderRadius: "999px",
                  border: isTailSourceSelected
                    ? "1px solid var(--brass, #C2AA7A)"
                    : "1px solid var(--line-2, #2E3645)",
                  background: isTailSourceSelected ? "var(--brass, #C2AA7A)" : "var(--ink-2, #181d26)",
                  color: isTailSourceSelected ? "var(--brass-ink, #17120A)" : "var(--fg-3, #8E8676)",
                  fontFamily: "var(--font-sans)",
                  fontSize: "var(--fs-12)",
                  fontWeight: "var(--w-medium)",
                  letterSpacing: "var(--tr-wide)",
                  cursor: "pointer",
                  transition: "background 150ms ease, color 150ms ease, border-color 150ms ease",
                  whiteSpace: "nowrap",
                }}
              >
                {isTailSourceSelected ? `${source} ▾` : `More (${tailSources.length}) ▾`}
              </button>
              {dropdownOpen && (
                <div
                  id="news-source-dropdown"
                  data-testid="news-source-dropdown"
                  style={{
                    position: "absolute",
                    top: "calc(100% + var(--sp-1))",
                    left: 0,
                    zIndex: 10,
                    minWidth: 180,
                    background: "var(--ink-2, #181d26)",
                    border: "1px solid var(--line-2, #2E3645)",
                    borderRadius: "var(--r-3)",
                    padding: "var(--sp-2)",
                    display: "flex",
                    flexDirection: "column",
                    gap: "var(--sp-1)",
                    boxShadow: "var(--shadow-2)",
                  }}
                >
                  {tailSources.map(([src, count]) => (
                    <button
                      key={src}
                      type="button"
                      onClick={() => {
                        setSource(src);
                        setDropdownOpen(false);
                      }}
                      style={{
                        textAlign: "left",
                        padding: "var(--sp-2) var(--sp-3)",
                        borderRadius: "var(--r-2)",
                        border: "none",
                        background: "transparent",
                        color: "var(--fg-2, #C7C0AE)",
                        fontFamily: "var(--font-sans)",
                        fontSize: "var(--fs-13)",
                        cursor: "pointer",
                        transition: "background 150ms ease, color 150ms ease",
                      }}
                      onMouseEnter={(e) => {
                        e.currentTarget.style.background = "var(--ink-3, #222934)";
                        e.currentTarget.style.color = "var(--fg-1, #F2ECDC)";
                      }}
                      onMouseLeave={(e) => {
                        e.currentTarget.style.background = "transparent";
                        e.currentTarget.style.color = "var(--fg-2, #C7C0AE)";
                      }}
                    >
                      {src} ({count})
                    </button>
                  ))}
                </div>
              )}
            </div>
          )}
        </div>

        <div data-testid="news-filter-sentiment" role="group" aria-label="Sentiment" style={{ display: "flex", gap: "var(--sp-2)", flexWrap: "wrap", alignItems: "center" }}>
          <span
            style={{
              fontFamily: "var(--font-mono)",
              fontSize: "var(--fs-11)",
              textTransform: "uppercase",
              letterSpacing: "var(--tr-wider)",
              color: "var(--fg-4, #5E5849)",
            }}
          >
            Sentiment
          </span>
          {sentiments.map((s) => (
            <FilterChip
              key={s}
              label={SENTIMENT_LABELS[s]}
              active={sentiment === s}
              onClick={() => setSentiment(s)}
            />
          ))}
        </div>
      </div>

      <div
        data-testid="news-readout"
        style={{
          fontFamily: "var(--font-mono)",
          fontSize: "var(--fs-13)",
          fontVariantNumeric: "tabular-nums",
          color: "var(--fg-3, #8E8676)",
          textAlign: "right",
          borderTop: "1px solid var(--line-1, #232934)",
          paddingTop: "var(--sp-3)",
        }}
      >
        {filtered.length} {filtered.length === 1 ? "article" : "articles"} · {sentimentCounts.bullish} bull · {sentimentCounts.neutral} neutral · {sentimentCounts.bearish} bear · {distinctSources} {distinctSources === 1 ? "source" : "sources"}
      </div>
    </div>
  );
}

function NewsCard({ item }: { item: NewsItem }) {
  const timestamp = formatNewsTimestamp(item.published_at);

  return (
    <a
      data-testid="news-card"
      href={item.url}
      target="_blank"
      rel="noopener noreferrer"
      className="psm-card"
      title={item.published_at}
      style={{
        display: "flex",
        flexDirection: "column",
        gap: "var(--sp-2)",
        textDecoration: "none",
        color: "inherit",
        transition: "border-color 150ms ease",
      }}
    >
      <div
        data-testid="news-card-title"
        style={{
          fontFamily: "var(--font-display)",
          fontSize: "var(--fs-18)",
          fontWeight: "var(--w-medium)",
          lineHeight: "var(--lh-snug)",
          color: "var(--fg-1, #F2ECDC)",
          display: "-webkit-box",
          WebkitLineClamp: 2,
          WebkitBoxOrient: "vertical",
          overflow: "hidden",
        }}
      >
        {item.title}
      </div>
      {item.summary && (
        <div
          style={{
            fontFamily: "var(--font-sans)",
            fontSize: "var(--fs-14)",
            lineHeight: "var(--lh-normal)",
            color: "var(--fg-3, #8E8676)",
            display: "-webkit-box",
            WebkitLineClamp: 2,
            WebkitBoxOrient: "vertical",
            overflow: "hidden",
          }}
        >
          {item.summary}
        </div>
      )}
      <div
        style={{
          display: "flex",
          alignItems: "center",
          gap: "var(--sp-2)",
          marginTop: "var(--sp-1)",
        }}
      >
        <span
          style={{
            fontFamily: "var(--font-mono)",
            fontSize: "var(--fs-12)",
            fontVariantNumeric: "tabular-nums",
            color: "var(--fg-3, #8E8676)",
          }}
        >
          {timestamp.display}
        </span>
        <span style={{ color: "var(--fg-4, #5E5849)" }}>·</span>
        <span
          data-testid="news-card-source"
          style={{
            fontFamily: "var(--font-sans)",
            fontSize: "var(--fs-12)",
            color: "var(--fg-3, #8E8676)",
          }}
        >
          {item.source}
        </span>
        <div style={{ marginLeft: "auto" }}>
          <span data-testid="news-card-sentiment">
            <SentimentChip sentiment={item.sentiment} label={SENTIMENT_LABELS[item.sentiment]} />
          </span>
        </div>
      </div>
    </a>
  );
}

export function NewsPage({ ticker, overview, isSaved, onToggleWatchlist }: Props) {
  const [data, setData] = useState<NewsResponse | null>(null);
  const [state, setState] = useState<NewsState>("loading");
  const [range, setRange] = useState<RangeFilter>("all");
  const [source, setSource] = useState<string>("all");
  const [sentiment, setSentiment] = useState<SentimentFilter>("all");
  const [rangeCutoff, setRangeCutoff] = useState<Record<RangeFilter, string | null>>(() => buildRangeCutoffs(Date.now()));
  const kpis = useMemo(() => buildKpis(overview), [overview]);

  useEffect(() => {
    let cancelled = false;
    setState("loading");
    setData(null);
    setRange("all");
    setSource("all");
    setSentiment("all");

    api
      .news(ticker)
      .then((res) => {
        if (cancelled) return;
        setRangeCutoff(buildRangeCutoffs(Date.now()));
        if (!res.items.length) {
          setData(res);
          setState("empty");
        } else {
          setData(res);
          setState("ready");
        }
      })
      .catch(() => {
        if (!cancelled) setState("error");
      });

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

  const filteredItems = useMemo(() => {
    if (!data) return [];
    return data.items.filter((item) => {
      const cutoff = rangeCutoff[range];
      if (cutoff != null && item.published_at < cutoff) return false;
      if (source !== "all" && item.source !== source) return false;
      if (sentiment !== "all" && item.sentiment !== sentiment) return false;
      return true;
    });
  }, [data, range, source, sentiment, rangeCutoff]);

  return (
    <div data-testid="news-page">
      <TickerHeader overview={overview} onToggleWatchlist={onToggleWatchlist} isSaved={isSaved} />
      <KPIStrip items={kpis} />

      <div style={{ padding: "0 var(--sp-1)" }}>
        {state === "loading" && <NewsSkeleton />}

        {state === "error" && (
          <div data-testid="news-error" className="psm-card" style={{ textAlign: "center", padding: "var(--sp-8)" }}>
            <div
              style={{
                fontFamily: "var(--font-sans)",
                fontSize: "var(--fs-14)",
                color: "var(--fg-3)",
              }}
            >
              Failed to load news
            </div>
          </div>
        )}

        {state === "empty" && data && (
          <div data-testid="news-empty" className="psm-card" style={{ textAlign: "center", padding: "var(--sp-8)" }}>
            <div
              style={{
                fontFamily: "var(--font-sans)",
                fontSize: "var(--fs-14)",
                color: "var(--fg-3)",
              }}
            >
              No recent news for {ticker}
            </div>
          </div>
        )}

        {state === "ready" && data && (
          <>
            <LedeCard
              ticker={ticker}
              itemCount={data.items.length}
              provider={data.provider}
              aggregate={data.aggregate}
            />
            <FilterBar
              items={data.items}
              range={range}
              setRange={setRange}
              source={source}
              setSource={setSource}
              sentiment={sentiment}
              setSentiment={setSentiment}
              rangeCutoff={rangeCutoff}
            />
            {filteredItems.length === 0 ? (
              <div
                data-testid="news-filter-empty"
                className="psm-card"
                style={{ textAlign: "center", padding: "var(--sp-8)" }}
              >
                <div
                  style={{
                    fontFamily: "var(--font-sans)",
                    fontSize: "var(--fs-14)",
                    color: "var(--fg-3)",
                    marginBottom: "var(--sp-2)",
                  }}
                >
                  No matches
                </div>
                <div
                  style={{
                    fontFamily: "var(--font-sans)",
                    fontSize: "var(--fs-13)",
                    color: "var(--fg-4)",
                  }}
                >
                  Try widening the date range or clearing filters.
                </div>
              </div>
            ) : (
              <div data-testid="news-feed" style={{ display: "flex", flexDirection: "column", gap: "var(--sp-3)" }}>
                {filteredItems.map((item) => (
                  <NewsCard key={item.id} item={item} />
                ))}
              </div>
            )}
          </>
        )}
      </div>

      <style>{`
        @keyframes psm-pulse {
          0%, 100% { opacity: 1; }
          50% { opacity: 0.55; }
        }
        [data-testid="news-card"]:hover,
        [data-testid="news-card"]:focus {
          border-color: var(--brass, #C2AA7A);
        }
      `}</style>
    </div>
  );
}
