import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { invoke } from '@tauri-apps/api/core';
import { listen, UnlistenFn } from '@tauri-apps/api/event';

/**
 * Sanitized error logging
 */
function logError(context: string, error: unknown): void {
  if (import.meta.env.PROD) {
    const errorType = error instanceof Error ? error.name : 'Unknown';
    console.error(`[Node] ${context}: ${errorType}`);
  } else {
    console.error(`[Node] ${context}:`, error);
  }
}

// ============================================================================
// Types
// ============================================================================

export type ConnectionMode =
  | { type: 'disconnected' }
  | { type: 'external'; http_url: string; ws_url?: string }
  | { type: 'embedded'; network: string; data_dir?: string };

export interface NodeStatus {
  mode: ConnectionMode;
  isConnected: boolean;
  blockHeight: number;
  blueScore: number;
  peerCount: number;
  isSyncing: boolean;
  syncProgress: number;
  network: string;
  chainId: number;
}

export interface SyncProgress {
  currentHeight: number;
  targetHeight: number;
  progress: number;
  etaSeconds?: number;
  status: string;
}

export interface PeerInfo {
  peerId: string;
  address: string;
  direction: 'inbound' | 'outbound';
  latencyMs?: number;
  blockHeight: number;
  status: string;
}

// ============================================================================
// Store
// ============================================================================

interface NodeState {
  // Status
  status: NodeStatus;
  syncProgress: SyncProgress | null;
  peers: PeerInfo[];

  // Connection preferences (persisted)
  preferredMode: 'external' | 'embedded';
  lastExternalUrl: string;
  lastNetwork: string;

  // Event listener cleanup
  _unlisteners: UnlistenFn[];

  // Actions
  setStatus: (status: NodeStatus) => void;
  setSyncProgress: (progress: SyncProgress | null) => void;
  setPeers: (peers: PeerInfo[]) => void;
  setPreferredMode: (mode: 'external' | 'embedded') => void;

  // Async actions
  connectExternal: (httpUrl: string, wsUrl?: string) => Promise<void>;
  startEmbeddedNode: (options: {
    network: string;
    dataDir?: string;
    miningEnabled?: boolean;
    coinbaseAddress?: string;
    miningThreads?: number;
  }) => Promise<void>;
  disconnect: () => Promise<void>;
  refreshStatus: () => Promise<void>;
  refreshPeers: () => Promise<void>;

  // Event listeners
  setupEventListeners: () => Promise<void>;
  cleanupEventListeners: () => void;
}

const initialStatus: NodeStatus = {
  mode: { type: 'disconnected' },
  isConnected: false,
  blockHeight: 0,
  blueScore: 0,
  peerCount: 0,
  isSyncing: false,
  syncProgress: 0,
  network: '',
  chainId: 0,
};

export const useNodeStore = create<NodeState>()(
  persist(
    (set, get) => ({
      // Initial state
      status: initialStatus,
      syncProgress: null,
      peers: [],
      preferredMode: 'external',
      lastExternalUrl: 'http://localhost:16110',
      lastNetwork: 'testnet',
      _unlisteners: [],

      // Sync setters
      setStatus: (status) => set({ status }),
      setSyncProgress: (progress) => set({ syncProgress: progress }),
      setPeers: (peers) => set({ peers }),
      setPreferredMode: (mode) => set({ preferredMode: mode }),

      // Async actions
      connectExternal: async (httpUrl: string, wsUrl?: string) => {
        try {
          const status = await invoke<NodeStatus>('node_connect_external', {
            httpUrl,
            wsUrl,
          });
          set({
            status: { ...status, isConnected: true },
            lastExternalUrl: httpUrl,
          });
        } catch (error) {
          logError('connectExternal', error);
          throw error;
        }
      },

      startEmbeddedNode: async (options) => {
        try {
          const status = await invoke<NodeStatus>('node_start_embedded', {
            network: options.network,
            dataDir: options.dataDir,
            miningEnabled: options.miningEnabled ?? false,
            coinbaseAddress: options.coinbaseAddress,
            miningThreads: options.miningThreads ?? 0,
          });
          set({
            status: { ...status, isConnected: true },
            lastNetwork: options.network,
            preferredMode: 'embedded',
          });
        } catch (error) {
          logError('startEmbeddedNode', error);
          throw error;
        }
      },

      disconnect: async () => {
        try {
          await invoke('node_stop');
          set({
            status: initialStatus,
            syncProgress: null,
            peers: [],
          });
        } catch (error) {
          logError('disconnect', error);
          throw error;
        }
      },

      refreshStatus: async () => {
        try {
          const status = await invoke<NodeStatus>('node_get_status');
          set({ status });
        } catch (error) {
          logError('refreshStatus', error);
        }
      },

      refreshPeers: async () => {
        try {
          const peers = await invoke<PeerInfo[]>('node_get_peers');
          set({ peers });
        } catch (error) {
          logError('refreshPeers', error);
        }
      },

      // Event listeners for real-time updates
      setupEventListeners: async () => {
        const unlisteners: UnlistenFn[] = [];

        // Listen for status changes
        const unlistenStatus = await listen<NodeStatus>(
          'node:status-changed',
          (event) => {
            set({ status: event.payload });
          }
        );
        unlisteners.push(unlistenStatus);

        // Listen for sync progress
        const unlistenSync = await listen<SyncProgress>(
          'node:sync-progress',
          (event) => {
            set({ syncProgress: event.payload });
          }
        );
        unlisteners.push(unlistenSync);

        // Listen for new blocks
        const unlistenBlock = await listen<{ height: number; hash: string }>(
          'node:new-block',
          (event) => {
            const current = get().status;
            set({
              status: {
                ...current,
                blockHeight: event.payload.height,
              },
            });
          }
        );
        unlisteners.push(unlistenBlock);

        // Listen for peer connections
        const unlistenPeer = await listen<PeerInfo>(
          'node:peer-connected',
          () => {
            // Refresh peer list when new peer connects
            get().refreshPeers();
          }
        );
        unlisteners.push(unlistenPeer);

        set({ _unlisteners: unlisteners });
      },

      cleanupEventListeners: () => {
        const { _unlisteners } = get();
        for (const unlisten of _unlisteners) {
          unlisten();
        }
        set({ _unlisteners: [] });
      },
    }),
    {
      name: 'synor-node-storage',
      partialize: (state) => ({
        preferredMode: state.preferredMode,
        lastExternalUrl: state.lastExternalUrl,
        lastNetwork: state.lastNetwork,
      }),
    }
  )
);

// ============================================================================
// Helper Hooks
// ============================================================================

/**
 * Returns true if connected to any node (embedded or external)
 */
export function useIsConnected(): boolean {
  return useNodeStore((state) => state.status.isConnected);
}

/**
 * Returns the current block height
 */
export function useBlockHeight(): number {
  return useNodeStore((state) => state.status.blockHeight);
}

/**
 * Returns the current sync status
 */
export function useIsSyncing(): boolean {
  return useNodeStore((state) => state.status.isSyncing);
}
