// Connectivity state store for offline support
//
// Pure reflection of the Rust ConnectivityMonitor. Reachability is the single
// source of truth in Rust, derived from real repository traffic (success/
// RepoError), with a time-window debounce before going offline and instant
// recovery. This store only listens for `connectivity:changed` events and
// mirrors status; it does not decide reachability itself. navigator.onLine is
// advisory and triggers a recheck rather than forcing offline.
// See docs/architecture/07-connectivity.md.
// TRACES: UR-002, UR-043, UR-052 | DR-013, DR-055, DR-079

import { writable, derived } from "svelte/store";
import { browser } from "$app/environment";
import { listen } from "@tauri-apps/api/event";
import { commands } from "$lib/api/bindings";

export interface ConnectivityState {
  /** Browser's navigator.onLine status */
  isOnline: boolean;
  /** Whether the Jellyfin server is actually reachable */
  isServerReachable: boolean;
  /** Last time we checked server reachability */
  lastChecked: Date | null;
  /** Error message from last connectivity check */
  connectionError: string | null;
  /** Whether we're currently checking connectivity */
  isChecking: boolean;
}

export interface ConnectivityEvents {
  /** Called when connectivity changes (online <-> offline) */
  onConnectivityChange?: (isConnected: boolean) => void;
  /** Called when server becomes reachable after being unreachable */
  onServerReconnected?: () => void;
}

function createConnectivityStore() {
  const initialState: ConnectivityState = {
    isOnline: browser ? navigator.onLine : true,
    // Start optimistic - assume server is reachable until proven otherwise
    // This prevents the app from appearing offline on startup
    isServerReachable: true,
    lastChecked: null,
    connectionError: null,
    isChecking: false,
  };

  const { subscribe, set, update } = writable<ConnectivityState>(initialState);

  let eventHandlers: ConnectivityEvents = {};
  let isMonitoring = false;

  // Listen to connectivity change events from Rust
  if (browser) {
    listen<{ isReachable: boolean }>("connectivity:changed", (event) => {
      update((s) => ({ ...s, isServerReachable: event.payload.isReachable }));

      if (eventHandlers.onConnectivityChange) {
        eventHandlers.onConnectivityChange(event.payload.isReachable);
      }
    });

    listen("connectivity:reconnected", () => {
      if (eventHandlers.onServerReconnected) {
        eventHandlers.onServerReconnected();
      }
    });

    // Listen to browser online/offline events. These are ADVISORY only — the
    // Rust ConnectivityMonitor (fed by real repository traffic) is the source of
    // truth for server reachability. navigator.onLine can be wrong (e.g. a
    // LAN-only server is still reachable while the browser reports "offline"),
    // so we use these events to trigger an immediate recheck rather than forcing
    // the offline state. See docs/architecture/07-connectivity.md.
    window.addEventListener("online", () => {
      update((s) => ({ ...s, isOnline: true }));
      // Device regained network — ask the backend to re-verify the server now.
      checkServerReachable().catch((err) => {
        console.debug("[ConnectivityStore] Recheck after 'online' failed:", err);
      });
    });

    window.addEventListener("offline", () => {
      // Reflect the browser's view of the device link, but let the backend
      // decide whether the server is actually reachable.
      update((s) => ({ ...s, isOnline: false }));
      checkServerReachable().catch((err) => {
        console.debug("[ConnectivityStore] Recheck after 'offline' failed:", err);
      });
    });
  }

  /**
   * Check if an error is a network error (vs auth/server error)
   * Kept for compatibility with existing code
   */
  function isNetworkError(error: unknown): boolean {
    if (error instanceof TypeError) {
      return true;
    }
    if (error instanceof Error) {
      const msg = error.message.toLowerCase();
      return (
        msg.includes("network") ||
        msg.includes("fetch") ||
        msg.includes("failed to fetch") ||
        msg.includes("networkerror") ||
        msg.includes("connection") ||
        msg.includes("timeout") ||
        msg.includes("aborted")
      );
    }
    return false;
  }

  /**
   * Check if the Jellyfin server is reachable (calls Rust)
   */
  async function checkServerReachable(): Promise<boolean> {
    try {
      const isReachable = await commands.connectivityCheckServer();

      // Fetch updated status from Rust
      const status = await commands.connectivityGetStatus();
      update((s) => ({
        ...s,
        isServerReachable: status.isServerReachable,
        lastChecked: status.lastChecked ? new Date(status.lastChecked) : null,
        connectionError: status.connectionError,
        isChecking: status.isChecking,
      }));

      return isReachable;
    } catch (error) {
      console.error("[ConnectivityStore] Failed to check server:", error);
      return false;
    }
  }

  /**
   * Start monitoring connectivity (delegates to Rust)
   */
  async function startMonitoring(url: string, handlers: ConnectivityEvents = {}): Promise<void> {
    eventHandlers = handlers;
    isMonitoring = true;

    try {
      console.log("[ConnectivityStore] Starting monitoring for:", url);

      // Set the server URL
      await commands.connectivitySetServerUrl(url);

      // Start the Rust monitoring task (performs immediate check)
      await commands.connectivityStartMonitoring();

      // Get the initial status immediately after starting
      const status = await commands.connectivityGetStatus();
      update((s) => ({
        ...s,
        isServerReachable: status.isServerReachable,
        lastChecked: status.lastChecked ? new Date(status.lastChecked) : null,
        connectionError: status.connectionError,
        isChecking: status.isChecking,
      }));

      console.log("[ConnectivityStore] Started monitoring. Initial status:",
        status.isServerReachable ? "ONLINE" : "OFFLINE");
    } catch (error) {
      console.error("[ConnectivityStore] Failed to start monitoring:", error);
      update((s) => ({
        ...s,
        isServerReachable: false,
        connectionError: "Failed to start monitoring",
      }));
    }
  }

  /**
   * Stop monitoring connectivity (delegates to Rust)
   */
  async function stopMonitoring(): Promise<void> {
    if (!isMonitoring) return;

    try {
      await commands.connectivityStopMonitoring();
      isMonitoring = false;
      eventHandlers = {};
      console.log("[ConnectivityStore] Stopped monitoring");
    } catch (error) {
      console.error("[ConnectivityStore] Failed to stop monitoring:", error);
    }
  }

  /**
   * Update server URL (call when user changes servers)
   */
  async function setServerUrl(url: string): Promise<void> {
    try {
      await commands.connectivitySetServerUrl(url);
    } catch (error) {
      console.error("[ConnectivityStore] Failed to set server URL:", error);
    }
  }

  /**
   * Force a connectivity check
   */
  async function forceCheck(): Promise<boolean> {
    return checkServerReachable();
  }

  // Reachability is now driven by the Rust backend from real repository
  // traffic (see docs/architecture/07-connectivity.md). The frontend no longer
  // mutates reachability itself, so the former markReachable/markUnreachable
  // helpers have been removed. The underlying Rust commands remain available
  // for deliberate signals if ever needed.

  return {
    subscribe,
    startMonitoring,
    stopMonitoring,
    setServerUrl,
    forceCheck,
    checkServerReachable,
    isNetworkError,
  };
}

export const connectivity = createConnectivityStore();

// Derived stores for convenience
export const isOnline = derived(connectivity, ($c) => $c.isOnline);
export const isServerReachable = derived(connectivity, ($c) => $c.isServerReachable);
// "Connected" follows backend reachability ALONE — not navigator.onLine.
// The Rust ConnectivityMonitor (fed by real repository traffic) is the source
// of truth (DR-055); navigator.onLine is advisory and can be wrong (server
// unreachable on a live device link — server down, wrong LAN, dropped VPN —
// still reports online). Folding it in kept `isConnected` true in exactly those
// cases and prevented the offline "downloaded only" gate from ever closing.
// navigator.onLine stays a *trigger* for a recheck (the online/offline
// listeners call checkServerReachable), never a *term* in this decision.
// The startup default (isServerReachable: true) is intentionally optimistic —
// a brief full-catalog flash before the first probe beats flipping the app to
// "offline" on launch. See docs/architecture/07-connectivity.md.
// TRACES: UR-052 | DR-079
export const isConnected = derived(
  connectivity,
  ($c) => $c.isServerReachable
);
export const connectionError = derived(connectivity, ($c) => $c.connectionError);
