import { Link, usePage } from "@inertiajs/react";
import tradingShow from "@/routes/trading";
import {
    Bell,
    CheckCheck,
    ExternalLink,
    RefreshCw,
    Settings2,
    Volume2,
    VolumeX,
    Monitor,
    MonitorOff,
} from "lucide-react";
import {
    useCallback,
    useEffect,
    useLayoutEffect,
    useRef,
    useState,
} from "react";
import { createPortal } from "react-dom";
import { Button } from "@/components/ui/button";
import { hasSubscriptionFeature } from "@/lib/subscription-access";
import type { PageProps } from "@inertiajs/core";

type SignalAlertEvent =
    | "detected"
    | "entry_reached"
    | "target_hit"
    | "invalidated";

type Notification = {
    id: number;
    event: SignalAlertEvent;
    symbol: string;
    provider: string;
    market: "spot" | "futures";
    interval: string;
    direction: string | null;
    title: string;
    message: string;
    read_at: string | null;
    created_at: string;
    event_at: number | null;
    event_candle_time: number | null;
    event_price: number | null;
};

type ResponseData = {
    notifications: Notification[];
    unread: number;
};

type SharedProps = PageProps & {
    subscription?: {
        active?: boolean | null;
        features?: readonly string[] | null;
    } | null;
};

const eventLabel: Record<SignalAlertEvent, string> = {
    detected: "Signal baru",
    entry_reached: "Entry tercapai",
    target_hit: "Target tercapai",
    invalidated: "Signal invalidated",
};

function csrf() {
    return (
        document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')
            ?.content ?? ""
    );
}

function getStoredBoolean(key: string, fallback: boolean): boolean {
    if (typeof window === "undefined") return fallback;
    return localStorage.getItem(key) === "1"
        ? true
        : localStorage.getItem(key) === "0"
          ? false
          : fallback;
}

function getStoredString(key: string, fallback: string): string {
    if (typeof window === "undefined") return fallback;
    return localStorage.getItem(key) ?? fallback;
}

function relativeTime(value: string) {
    const seconds = Math.max(
        0,
        Math.floor((Date.now() - new Date(value).getTime()) / 1000),
    );
    if (seconds < 60) return `${seconds}s`;
    const minutes = Math.floor(seconds / 60);
    if (minutes < 60) return `${minutes}m`;
    const hours = Math.floor(minutes / 60);
    if (hours < 24) return `${hours}h`;
    return `${Math.floor(hours / 24)}d`;
}

export default function SignalAlertCenter() {
    const { subscription } = usePage<SharedProps>().props;
    const canUseAdvancedAlerts = hasSubscriptionFeature(
        subscription,
        "advanced_alerts",
    );

    const [open, setOpen] = useState(false);
    const [data, setData] = useState<ResponseData>({
        notifications: [],
        unread: 0,
    });
    const [loading, setLoading] = useState(false);
    const requestInFlightRef = useRef(false);
    const [toast, setToast] = useState<Notification | null>(null);
    const [soundEnabled, setSoundEnabled] = useState(() =>
        getStoredBoolean("signal-alert-sound", true),
    );
    const [browserEnabled, setBrowserEnabled] = useState(() =>
        getStoredBoolean("signal-alert-browser", false),
    );
    const [quietHoursEnabled, setQuietHoursEnabled] = useState(() =>
        getStoredBoolean("signal-alert-quiet", false),
    );
    const [quietStart, setQuietStart] = useState(() =>
        getStoredString("signal-alert-quiet-start", "23:00"),
    );
    const [quietEnd, setQuietEnd] = useState(() =>
        getStoredString("signal-alert-quiet-end", "07:00"),
    );
    const knownIdsRef = useRef<Set<number> | null>(null);
    const audioContextRef = useRef<AudioContext | null>(null);
    const triggerRef = useRef<HTMLButtonElement>(null);
    const menuRef = useRef<HTMLDivElement>(null);
    const [menuPosition, setMenuPosition] = useState({ top: 0, right: 16 });

    const isQuietHours = useCallback(() => {
        if (!quietHoursEnabled) return false;
        const now = new Date();
        const current = now.getHours() * 60 + now.getMinutes();
        const [startHour, startMinute] = quietStart.split(":").map(Number);
        const [endHour, endMinute] = quietEnd.split(":").map(Number);
        const start = startHour * 60 + startMinute;
        const end = endHour * 60 + endMinute;
        if (start === end) return true;
        return start < end
            ? current >= start && current < end
            : current >= start || current < end;
    }, [quietEnd, quietHoursEnabled, quietStart]);

    const playAlertSound = useCallback(() => {
        if (!soundEnabled || isQuietHours()) return;
        try {
            const AudioContextClass =
                window.AudioContext ||
                (
                    window as typeof window & {
                        webkitAudioContext?: typeof AudioContext;
                    }
                ).webkitAudioContext;
            if (!AudioContextClass) return;
            const context = audioContextRef.current ?? new AudioContextClass();
            audioContextRef.current = context;
            if (context.state === "suspended") void context.resume();
            const oscillator = context.createOscillator();
            const gain = context.createGain();
            oscillator.type = "sine";
            oscillator.frequency.setValueAtTime(880, context.currentTime);
            oscillator.frequency.exponentialRampToValueAtTime(
                660,
                context.currentTime + 0.16,
            );
            gain.gain.setValueAtTime(0.0001, context.currentTime);
            gain.gain.exponentialRampToValueAtTime(
                0.08,
                context.currentTime + 0.015,
            );
            gain.gain.exponentialRampToValueAtTime(
                0.0001,
                context.currentTime + 0.18,
            );
            oscillator.connect(gain);
            gain.connect(context.destination);
            oscillator.start();
            oscillator.stop(context.currentTime + 0.2);
        } catch {
            // Browser audio is optional; notification delivery must continue.
        }
    }, [isQuietHours, soundEnabled]);

    const deliverNewNotifications = useCallback(
        (notifications: Notification[]) => {
            const ids = new Set(notifications.map((item) => item.id));
            const previous = knownIdsRef.current;
            knownIdsRef.current = ids;
            if (!previous) return;
            const newest = notifications.filter(
                (item) => !previous.has(item.id) && !item.read_at,
            )[0];
            if (!newest || isQuietHours()) return;
            setToast(newest);
            playAlertSound();
            if (
                browserEnabled &&
                "Notification" in window &&
                Notification.permission === "granted"
            ) {
                new Notification(newest.title, {
                    body: newest.message,
                    tag: `signal-alert-${newest.id}`,
                });
            }
            window.setTimeout(
                () =>
                    setToast((current) =>
                        current?.id === newest.id ? null : current,
                    ),
                6000,
            );
        },
        [browserEnabled, isQuietHours, playAlertSound],
    );

    const load = useCallback(
        async (silent = false) => {
            // One alerts request at a time prevents the initial StrictMode
            // effect replay and the polling timer from stacking identical GETs.
            if (
                !canUseAdvancedAlerts ||
                requestInFlightRef.current ||
                document.hidden
            )
                return;
            requestInFlightRef.current = true;
            if (!silent) setLoading(true);
            const controller = new AbortController();
            const timeout = window.setTimeout(() => controller.abort(), 15_000);
            try {
                const response = await fetch("/signal-alerts", {
                    headers: { Accept: "application/json" },
                    credentials: "same-origin",
                    signal: controller.signal,
                });
                if (!response.ok) return;
                const payload = (await response.json()) as ResponseData;
                const notifications = Array.isArray(payload.notifications)
                    ? payload.notifications
                    : [];
                deliverNewNotifications(notifications);
                setData({
                    notifications,
                    unread: Number(payload.unread ?? 0),
                });
            } catch {
                // Keep the last known notifications on transient network errors.
            } finally {
                window.clearTimeout(timeout);
                requestInFlightRef.current = false;
                if (!silent) setLoading(false);
            }
        },
        [canUseAdvancedAlerts, deliverNewNotifications],
    );

    useEffect(() => {
        if (!canUseAdvancedAlerts) {
            setLoading(false);
            setOpen(false);
            setToast(null);
            setData({ notifications: [], unread: 0 });
            return;
        }

        void load();
        const timer = window.setInterval(() => {
            if (!document.hidden) void load(true);
        }, 30_000);
        const handleVisibilityChange = () => {
            if (!document.hidden) void load(true);
        };
        document.addEventListener("visibilitychange", handleVisibilityChange);
        return () => {
            window.clearInterval(timer);
            document.removeEventListener(
                "visibilitychange",
                handleVisibilityChange,
            );
        };
    }, [canUseAdvancedAlerts, load]);

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

        const handlePointerDown = (event: PointerEvent) => {
            const target = event.target as Node | null;
            const menu = menuRef.current;
            const trigger = triggerRef.current;

            if (
                target &&
                (menu?.contains(target) || trigger?.contains(target))
            ) {
                return;
            }

            setOpen(false);
        };

        const handleKeyDown = (event: KeyboardEvent) => {
            if (event.key === "Escape") {
                setOpen(false);
                triggerRef.current?.focus();
            }
        };

        document.addEventListener("pointerdown", handlePointerDown);
        document.addEventListener("keydown", handleKeyDown);

        return () => {
            document.removeEventListener("pointerdown", handlePointerDown);
            document.removeEventListener("keydown", handleKeyDown);
        };
    }, [open]);

    useLayoutEffect(() => {
        if (!open) return;

        const updatePosition = () => {
            const trigger = triggerRef.current;
            if (!trigger) return;

            const rect = trigger.getBoundingClientRect();
            setMenuPosition({
                top: rect.bottom + 8,
                right: Math.max(16, window.innerWidth - rect.right),
            });
        };

        updatePosition();
        window.addEventListener("resize", updatePosition);
        window.addEventListener("scroll", updatePosition, true);

        return () => {
            window.removeEventListener("resize", updatePosition);
            window.removeEventListener("scroll", updatePosition, true);
        };
    }, [open]);

    const markRead = async (notification: Notification) => {
        if (!canUseAdvancedAlerts || notification.read_at) return;
        await fetch(`/signal-alerts/notifications/${notification.id}/read`, {
            method: "POST",
            headers: {
                Accept: "application/json",
                "X-CSRF-TOKEN": csrf(),
            },
            credentials: "same-origin",
        });
        setData((current) => ({
            notifications: current.notifications.map((item) =>
                item.id === notification.id
                    ? { ...item, read_at: new Date().toISOString() }
                    : item,
            ),
            unread: Math.max(0, current.unread - 1),
        }));
    };

    const openNotification = async (notification: Notification) => {
        await markRead(notification);

        const url = tradingShow.show.url(notification.symbol, {
            query: {
                market: notification.market,
                provider: notification.provider || "binance",
                interval: notification.interval,
                ...(notification.event_candle_time || notification.event_at
                    ? {
                          focus_time:
                              notification.event_candle_time ??
                              notification.event_at,
                          focus_event: notification.event,
                          ...(notification.event_price != null
                              ? { focus_price: notification.event_price }
                              : {}),
                      }
                    : { focus_event: notification.event }),
            },
        });

        window.location.assign(url);
    };

    const markAllRead = async () => {
        if (!canUseAdvancedAlerts || !data.unread) return;
        await fetch("/signal-alerts/notifications/read-all", {
            method: "POST",
            headers: {
                Accept: "application/json",
                "X-CSRF-TOKEN": csrf(),
            },
            credentials: "same-origin",
        });
        setData((current) => ({
            notifications: current.notifications.map((item) => ({
                ...item,
                read_at: item.read_at ?? new Date().toISOString(),
            })),
            unread: 0,
        }));
    };

    const persistDelivery = (key: string, value: string) => {
        localStorage.setItem(key, value);
    };

    const requestBrowserPermission = async () => {
        if (!("Notification" in window)) return;
        const permission = await Notification.requestPermission();
        const enabled = permission === "granted";
        setBrowserEnabled(enabled);
        persistDelivery("signal-alert-browser", enabled ? "1" : "0");
    };

    return (
        <>
            {toast && typeof document !== "undefined"
                ? createPortal(
                      <button
                          type="button"
                          onClick={() => void openNotification(toast)}
                          className="fixed right-4 top-4 z-120 w-90 max-w-[calc(100vw-2rem)] rounded-2xl border border-border bg-popover px-4 py-3 text-left shadow-2xl"
                      >
                          <div className="text-sm font-semibold">
                              {toast.title}
                          </div>
                          <div className="mt-1 text-xs leading-5 text-muted-foreground">
                              {toast.message}
                          </div>
                          <div className="mt-2 text-[10px] font-medium uppercase tracking-wide text-emerald-400">
                              Open Analytic Terminal
                          </div>
                      </button>,
                      document.body,
                  )
                : null}
            <div className="relative">
                <Button
                    ref={triggerRef}
                    type="button"
                    variant="ghost"
                    size="icon"
                    aria-label="Notifications"
                    aria-expanded={open}
                    onClick={() => {
                        if (!canUseAdvancedAlerts) return;
                        setOpen((value) => !value);
                        if (!open) void load(true);
                    }}
                    disabled={!canUseAdvancedAlerts}
                    className="relative size-10 text-muted-foreground hover:bg-accent hover:text-foreground sm:size-8"
                >
                    <Bell className="size-4" />
                    {data.unread > 0 && (
                        <span className="absolute right-1 top-1 flex min-w-3.5 items-center justify-center rounded-full bg-emerald-400 px-1 text-[9px] font-bold leading-3 text-[#04120d]">
                            {data.unread > 99 ? "99+" : data.unread}
                        </span>
                    )}
                </Button>

                {open && typeof document !== "undefined"
                    ? createPortal(
                          <div
                              ref={menuRef}
                              className="fixed z-100 w-90 max-w-[calc(100vw-2rem)] overflow-hidden rounded-2xl border border-border bg-popover shadow-2xl backdrop-blur-none"
                              style={{
                                  top: menuPosition.top,
                                  right: menuPosition.right,
                              }}
                          >
                              <div className="flex items-center justify-between border-b border-border px-4 py-3">
                                  <div>
                                      <div className="font-semibold">
                                          Notifications
                                      </div>
                                      <div className="text-[11px] text-muted-foreground">
                                          Signal Engine lifecycle
                                      </div>
                                  </div>
                                  <div className="flex items-center gap-1">
                                      <Button
                                          type="button"
                                          variant="ghost"
                                          size="icon"
                                          className="size-7"
                                          onClick={() => void load()}
                                          disabled={loading}
                                          aria-label="Refresh notifications"
                                      >
                                          <RefreshCw
                                              className={
                                                  loading
                                                      ? "size-3.5 animate-spin"
                                                      : "size-3.5"
                                              }
                                          />
                                      </Button>
                                      {data.unread > 0 && (
                                          <Button
                                              type="button"
                                              variant="ghost"
                                              size="sm"
                                              className="h-7 gap-1 px-2 text-xs"
                                              onClick={() => void markAllRead()}
                                          >
                                              <CheckCheck className="size-3.5" />
                                              Mark all read
                                          </Button>
                                      )}
                                  </div>
                              </div>

                              <div className="border-b border-border px-4 py-3">
                                  <div className="mb-2 flex items-center gap-2 text-xs font-semibold">
                                      <Settings2 className="size-3.5" />
                                      Delivery
                                  </div>
                                  <div className="grid grid-cols-2 gap-2 text-[11px]">
                                      <button
                                          type="button"
                                          className="flex items-center gap-2 rounded-lg border border-border px-2 py-1.5 text-left"
                                          onClick={() => {
                                              const next = !soundEnabled;
                                              setSoundEnabled(next);
                                              persistDelivery(
                                                  "signal-alert-sound",
                                                  next ? "1" : "0",
                                              );
                                              if (next) playAlertSound();
                                          }}
                                      >
                                          {soundEnabled ? (
                                              <Volume2 className="size-3.5" />
                                          ) : (
                                              <VolumeX className="size-3.5" />
                                          )}{" "}
                                          Sound
                                      </button>
                                      <button
                                          type="button"
                                          className="flex items-center gap-2 rounded-lg border border-border px-2 py-1.5 text-left"
                                          onClick={() => {
                                              if (browserEnabled) {
                                                  setBrowserEnabled(false);
                                                  persistDelivery(
                                                      "signal-alert-browser",
                                                      "0",
                                                  );
                                              } else {
                                                  void requestBrowserPermission();
                                              }
                                          }}
                                      >
                                          {browserEnabled ? (
                                              <Monitor className="size-3.5" />
                                          ) : (
                                              <MonitorOff className="size-3.5" />
                                          )}{" "}
                                          Browser
                                      </button>
                                  </div>
                                  <div className="mt-2 flex items-center gap-2 text-[11px]">
                                      <label className="flex items-center gap-2">
                                          <input
                                              type="checkbox"
                                              checked={quietHoursEnabled}
                                              onChange={(event) => {
                                                  const next =
                                                      event.target.checked;
                                                  setQuietHoursEnabled(next);
                                                  persistDelivery(
                                                      "signal-alert-quiet",
                                                      next ? "1" : "0",
                                                  );
                                              }}
                                          />
                                          Quiet hours
                                      </label>
                                      {quietHoursEnabled && (
                                          <>
                                              <input
                                                  type="time"
                                                  value={quietStart}
                                                  onChange={(event) => {
                                                      setQuietStart(
                                                          event.target.value,
                                                      );
                                                      persistDelivery(
                                                          "signal-alert-quiet-start",
                                                          event.target.value,
                                                      );
                                                  }}
                                                  className="h-7 rounded border border-border bg-background px-1 text-[10px]"
                                              />
                                              <span>–</span>
                                              <input
                                                  type="time"
                                                  value={quietEnd}
                                                  onChange={(event) => {
                                                      setQuietEnd(
                                                          event.target.value,
                                                      );
                                                      persistDelivery(
                                                          "signal-alert-quiet-end",
                                                          event.target.value,
                                                      );
                                                  }}
                                                  className="h-7 rounded border border-border bg-background px-1 text-[10px]"
                                              />
                                          </>
                                      )}
                                  </div>
                              </div>

                              <div className="max-h-105 overflow-y-auto">
                                  {data.notifications.length === 0 ? (
                                      <div className="px-5 py-10 text-center text-sm text-muted-foreground">
                                          No signal notifications yet.
                                      </div>
                                  ) : (
                                      data.notifications.map((notification) => (
                                          <button
                                              key={notification.id}
                                              type="button"
                                              onClick={() =>
                                                  void openNotification(
                                                      notification,
                                                  )
                                              }
                                              className={`w-full border-b border-border/70 px-4 py-3 text-left transition hover:bg-muted/50 ${
                                                  notification.read_at
                                                      ? "opacity-70"
                                                      : "bg-emerald-400/4.5"
                                              }`}
                                          >
                                              <div className="flex items-start gap-3">
                                                  <span
                                                      className={`mt-1 size-2 shrink-0 rounded-full ${notification.read_at ? "bg-muted-foreground/30" : "bg-emerald-400"}`}
                                                  />
                                                  <div className="min-w-0 flex-1">
                                                      <div className="flex items-start justify-between gap-2">
                                                          <div className="truncate text-sm font-semibold">
                                                              {
                                                                  notification.title
                                                              }
                                                          </div>
                                                          <span className="shrink-0 text-[10px] text-muted-foreground">
                                                              {relativeTime(
                                                                  notification.created_at,
                                                              )}
                                                          </span>
                                                      </div>
                                                      <div className="mt-0.5 text-[11px] font-medium text-emerald-400">
                                                          {
                                                              eventLabel[
                                                                  notification
                                                                      .event
                                                              ]
                                                          }
                                                      </div>
                                                      <p className="mt-1 line-clamp-2 text-xs leading-5 text-muted-foreground">
                                                          {notification.message}
                                                      </p>
                                                      <div className="mt-2 flex flex-wrap items-center gap-2 text-[10px] uppercase tracking-wide text-muted-foreground">
                                                          <span>
                                                              {notification.provider ||
                                                                  "binance"}
                                                          </span>
                                                          <span>·</span>
                                                          <span>
                                                              {
                                                                  notification.market
                                                              }
                                                          </span>
                                                          <span>·</span>
                                                          <span>
                                                              {
                                                                  notification.interval
                                                              }
                                                          </span>
                                                          <span>·</span>
                                                          <span>
                                                              {notification.direction ??
                                                                  "—"}
                                                          </span>
                                                      </div>
                                                  </div>
                                              </div>
                                          </button>
                                      ))
                                  )}
                              </div>

                              <div className="border-t border-border p-2">
                                  <Link
                                      href="/alerts"
                                      onClick={() => setOpen(false)}
                                      className="flex items-center justify-center gap-1 rounded-lg px-3 py-2 text-xs font-semibold text-muted-foreground transition hover:bg-muted hover:text-foreground"
                                  >
                                      Open Alert Center
                                      <ExternalLink className="size-3" />
                                  </Link>
                              </div>
                          </div>,
                          document.body,
                      )
                    : null}
            </div>
        </>
    );
}
