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(`[Mining] ${context}: ${errorType}`);
  } else {
    console.error(`[Mining] ${context}:`, error);
  }
}

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

export interface MiningStatus {
  isMining: boolean;
  isPaused: boolean;
  hashrate: number;
  blocksFound: number;
  sharesSubmitted: number;
  threads: number;
  coinbaseAddress?: string;
}

export interface MiningStats {
  hashrate: number;
  avgHashrate: number;
  peakHashrate: number;
  blocksFound: number;
  blocksRejected: number;
  estimatedDailyCoins: number;
  uptimeSeconds: number;
  threadHashrates: number[];
}

export interface BlockFoundEvent {
  height: number;
  hash: string;
  reward: number;
  timestamp: number;
}

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

interface MiningState {
  // Status
  status: MiningStatus;
  stats: MiningStats | null;
  recentBlocks: BlockFoundEvent[];
  hashrateHistory: { timestamp: number; hashrate: number }[];

  // Settings (persisted)
  defaultThreads: number;
  defaultCoinbaseAddress: string;
  autoStartMining: boolean;

  // Event listener cleanup
  _unlisteners: UnlistenFn[];

  // Actions
  setStatus: (status: MiningStatus) => void;
  setStats: (stats: MiningStats | null) => void;
  addBlockFound: (block: BlockFoundEvent) => void;
  addHashratePoint: (hashrate: number) => void;
  setDefaultThreads: (threads: number) => void;
  setDefaultCoinbaseAddress: (address: string) => void;
  setAutoStartMining: (autoStart: boolean) => void;

  // Async actions
  startMining: (coinbaseAddress: string, threads: number) => Promise<void>;
  stopMining: () => Promise<void>;
  pauseMining: () => Promise<void>;
  resumeMining: () => Promise<void>;
  setThreads: (threads: number) => Promise<void>;
  refreshStatus: () => Promise<void>;
  refreshStats: () => Promise<void>;

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

const initialStatus: MiningStatus = {
  isMining: false,
  isPaused: false,
  hashrate: 0,
  blocksFound: 0,
  sharesSubmitted: 0,
  threads: 0,
  coinbaseAddress: undefined,
};

export const useMiningStore = create<MiningState>()(
  persist(
    (set, get) => ({
      // Initial state
      status: initialStatus,
      stats: null,
      recentBlocks: [],
      hashrateHistory: [],
      defaultThreads: navigator.hardwareConcurrency || 4,
      defaultCoinbaseAddress: '',
      autoStartMining: false,
      _unlisteners: [],

      // Sync setters
      setStatus: (status) => set({ status }),
      setStats: (stats) => set({ stats }),

      addBlockFound: (block) =>
        set((state) => ({
          recentBlocks: [block, ...state.recentBlocks].slice(0, 50), // Keep last 50
        })),

      addHashratePoint: (hashrate) =>
        set((state) => ({
          hashrateHistory: [
            ...state.hashrateHistory,
            { timestamp: Date.now(), hashrate },
          ].slice(-300), // Keep last 5 minutes (assuming 1s intervals)
        })),

      setDefaultThreads: (threads) => set({ defaultThreads: threads }),
      setDefaultCoinbaseAddress: (address) =>
        set({ defaultCoinbaseAddress: address }),
      setAutoStartMining: (autoStart) => set({ autoStartMining: autoStart }),

      // Async actions
      startMining: async (coinbaseAddress: string, threads: number) => {
        try {
          const status = await invoke<MiningStatus>('mining_start', {
            coinbaseAddress,
            threads,
          });
          set({
            status,
            defaultCoinbaseAddress: coinbaseAddress,
            defaultThreads: threads,
          });
        } catch (error) {
          logError('startMining', error);
          throw error;
        }
      },

      stopMining: async () => {
        try {
          await invoke('mining_stop');
          set({
            status: initialStatus,
            stats: null,
          });
        } catch (error) {
          logError('stopMining', error);
          throw error;
        }
      },

      pauseMining: async () => {
        try {
          await invoke('mining_pause');
          set((state) => ({
            status: { ...state.status, isPaused: true },
          }));
        } catch (error) {
          logError('pauseMining', error);
          throw error;
        }
      },

      resumeMining: async () => {
        try {
          await invoke('mining_resume');
          set((state) => ({
            status: { ...state.status, isPaused: false },
          }));
        } catch (error) {
          logError('resumeMining', error);
          throw error;
        }
      },

      setThreads: async (threads: number) => {
        try {
          await invoke('mining_set_threads', { threads });
          set((state) => ({
            status: { ...state.status, threads },
            defaultThreads: threads,
          }));
        } catch (error) {
          logError('setThreads', error);
          throw error;
        }
      },

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

      refreshStats: async () => {
        try {
          const stats = await invoke<MiningStats>('mining_get_stats');
          set({ stats });
          // Also add to hashrate history
          get().addHashratePoint(stats.hashrate);
        } catch (error) {
          logError('refreshStats', error);
        }
      },

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

        // Listen for mining stats updates
        const unlistenStats = await listen<MiningStats>(
          'mining:stats-update',
          (event) => {
            set({ stats: event.payload });
            get().addHashratePoint(event.payload.hashrate);
          }
        );
        unlisteners.push(unlistenStats);

        // Listen for block found events
        const unlistenBlock = await listen<BlockFoundEvent>(
          'mining:block-found',
          (event) => {
            get().addBlockFound(event.payload);
            // Refresh status to update block count
            get().refreshStatus();
          }
        );
        unlisteners.push(unlistenBlock);

        set({ _unlisteners: unlisteners });
      },

      cleanupEventListeners: () => {
        const { _unlisteners } = get();
        for (const unlisten of _unlisteners) {
          unlisten();
        }
        set({ _unlisteners: [] });
      },
    }),
    {
      name: 'synor-mining-storage',
      partialize: (state) => ({
        defaultThreads: state.defaultThreads,
        defaultCoinbaseAddress: state.defaultCoinbaseAddress,
        autoStartMining: state.autoStartMining,
      }),
    }
  )
);

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

/**
 * Returns true if currently mining (and not paused)
 */
export function useIsMiningActive(): boolean {
  return useMiningStore((state) => state.status.isMining && !state.status.isPaused);
}

/**
 * Returns the current hashrate
 */
export function useHashrate(): number {
  return useMiningStore((state) => state.status.hashrate);
}

/**
 * Returns total blocks found
 */
export function useBlocksFound(): number {
  return useMiningStore((state) => state.status.blocksFound);
}

/**
 * Formats hashrate for display
 */
export function formatHashrate(hashrate: number): string {
  if (hashrate < 1000) {
    return `${hashrate.toFixed(2)} H/s`;
  } else if (hashrate < 1_000_000) {
    return `${(hashrate / 1000).toFixed(2)} KH/s`;
  } else if (hashrate < 1_000_000_000) {
    return `${(hashrate / 1_000_000).toFixed(2)} MH/s`;
  } else {
    return `${(hashrate / 1_000_000_000).toFixed(2)} GH/s`;
  }
}
