import { useCallback, useEffect, useRef, useState } from 'react';
import { terminalBridge } from '../lib/terminalBridge';
import { companyToTickerHistorySnapshot } from '../lib/tickerHistory';
import {
  TickerHistoryEntry,
  TickerHistorySnapshot,
} from '../types/terminal';

const STORAGE_KEY = 'ticker_history';
const MAX_HISTORY = 7;

type StoredTickerHistoryEntry = {
  company?: Partial<TickerHistorySnapshot>;
  symbol?: string;
  timestamp?: string;
};

const normalizeSymbol = (symbol: string): string => symbol.trim().toUpperCase();

const normalizeSnapshot = (
  snapshot: Partial<TickerHistorySnapshot> & { symbol: string },
): TickerHistorySnapshot => {
  const symbol = normalizeSymbol(snapshot.symbol);

  return {
    symbol,
    name: snapshot.name?.trim() || symbol,
    price: snapshot.price,
    change: snapshot.change,
    changePercent: snapshot.changePercent,
    volume: snapshot.volume,
    marketCap: snapshot.marketCap,
  };
};

const mergeSnapshot = (
  existing: TickerHistorySnapshot,
  incoming: TickerHistorySnapshot,
): TickerHistorySnapshot => ({
  symbol: incoming.symbol,
  name: incoming.name || existing.name,
  price: incoming.price ?? existing.price,
  change: incoming.change ?? existing.change,
  changePercent: incoming.changePercent ?? existing.changePercent,
  volume: incoming.volume ?? existing.volume,
  marketCap: incoming.marketCap ?? existing.marketCap,
});

const upsertHistory = (
  history: TickerHistoryEntry[],
  snapshot: TickerHistorySnapshot,
  options?: {
    timestamp?: Date;
    isLoading?: boolean;
  },
): TickerHistoryEntry[] => {
  const timestamp = options?.timestamp ?? new Date();
  const normalizedSymbol = normalizeSymbol(snapshot.symbol);
  const normalizedSnapshot = normalizeSnapshot({
    ...snapshot,
    symbol: normalizedSymbol,
  });

  const existingEntry = history.find(
    (entry) => entry.company.symbol === normalizedSymbol,
  );

  const nextEntry: TickerHistoryEntry = {
    company: existingEntry
      ? mergeSnapshot(existingEntry.company, normalizedSnapshot)
      : normalizedSnapshot,
    timestamp,
    isLoading: options?.isLoading ?? existingEntry?.isLoading ?? false,
  };

  const deduped = history.filter(
    (entry) => entry.company.symbol !== normalizedSymbol,
  );

  return [nextEntry, ...deduped]
    .sort((left, right) => right.timestamp.getTime() - left.timestamp.getTime())
    .slice(0, MAX_HISTORY);
};

const setEntryLoadingState = (
  history: TickerHistoryEntry[],
  symbol: string,
  isLoading: boolean,
): TickerHistoryEntry[] =>
  history.map((entry) =>
    entry.company.symbol === symbol
      ? { ...entry, isLoading }
      : entry,
  );

const deserializeHistoryEntry = (
  entry: StoredTickerHistoryEntry,
): TickerHistoryEntry | null => {
  const symbol = entry.company?.symbol ?? entry.symbol;
  const normalizedSymbol = symbol ? normalizeSymbol(symbol) : '';

  if (!normalizedSymbol) {
    return null;
  }

  const timestamp = entry.timestamp ? new Date(entry.timestamp) : new Date();
  const normalizedTimestamp = Number.isNaN(timestamp.getTime())
    ? new Date()
    : timestamp;

  return {
    company: normalizeSnapshot({
      ...(entry.company ?? {}),
      symbol: normalizedSymbol,
    }),
    timestamp: normalizedTimestamp,
    isLoading: false,
  };
};

export const useTickerHistory = () => {
  const [history, setHistory] = useState<TickerHistoryEntry[]>([]);
  const [isLoaded, setIsLoaded] = useState(false);
  const historyRef = useRef<TickerHistoryEntry[]>([]);
  const hasRefreshedOnLoadRef = useRef(false);

  historyRef.current = history;

  useEffect(() => {
    try {
      const stored = localStorage.getItem(STORAGE_KEY);

      if (stored) {
        const parsed = JSON.parse(stored) as StoredTickerHistoryEntry[] | unknown;
        const loadedHistory = (Array.isArray(parsed) ? parsed : [])
          .map(deserializeHistoryEntry)
          .filter(
            (entry): entry is TickerHistoryEntry => entry !== null,
          )
          .slice(0, MAX_HISTORY);

        setHistory(loadedHistory);
      }
    } catch (error) {
      console.error('Failed to load ticker history from localStorage:', error);
    } finally {
      setIsLoaded(true);
    }
  }, []);

  useEffect(() => {
    if (!isLoaded) {
      return;
    }

    try {
      localStorage.setItem(
        STORAGE_KEY,
        JSON.stringify(
          history.map((entry) => ({
            company: entry.company,
            symbol: entry.company.symbol,
            timestamp: entry.timestamp.toISOString(),
          })),
        ),
      );
    } catch (error) {
      console.error('Failed to save ticker history to localStorage:', error);
    }
  }, [history, isLoaded]);

  const refreshHistoryQuotes = useCallback(async () => {
    const entries = historyRef.current;

    if (entries.length === 0) {
      return;
    }

    setHistory((prev) => prev.map((entry) => ({ ...entry, isLoading: true })));

    await Promise.allSettled(
      entries.map(async (entry) => {
        const symbol = entry.company.symbol;
        const company = await terminalBridge.lookupCompany({
          symbol,
        });

        setHistory((prev) =>
          upsertHistory(prev, companyToTickerHistorySnapshot(company), {
            timestamp: entry.timestamp,
            isLoading: false,
          }),
        );
      }),
    ).then((results) => {
      results.forEach((result, index) => {
        if (result.status === 'rejected') {
          const symbol = entries[index]?.company.symbol;
          if (symbol) {
            setHistory((prev) => setEntryLoadingState(prev, symbol, false));
          }
          console.error('Failed to refresh ticker history quote:', result.reason);
        }
      });
    });
  }, []);

  useEffect(() => {
    if (!isLoaded || hasRefreshedOnLoadRef.current) {
      return;
    }

    hasRefreshedOnLoadRef.current = true;
    void refreshHistoryQuotes();
  }, [isLoaded, refreshHistoryQuotes]);

  const recordTicker = useCallback(async (symbol: string) => {
    const normalizedSymbol = normalizeSymbol(symbol);
    const recordedAt = new Date();

    if (!normalizedSymbol) {
      return;
    }

    setHistory((prev) =>
      upsertHistory(
        prev,
        normalizeSnapshot({
          symbol: normalizedSymbol,
          name: normalizedSymbol,
        }),
        {
          timestamp: recordedAt,
          isLoading: true,
        },
      ),
    );

    try {
      const company = await terminalBridge.lookupCompany({
        symbol: normalizedSymbol,
      });

      setHistory((prev) =>
        upsertHistory(prev, companyToTickerHistorySnapshot(company), {
          timestamp: recordedAt,
          isLoading: false,
        }),
      );
    } catch (error) {
      setHistory((prev) => setEntryLoadingState(prev, normalizedSymbol, false));
      console.error(`Failed to record ticker history for ${normalizedSymbol}:`, error);
    }
  }, []);

  const clearHistory = useCallback(() => {
    setHistory([]);
  }, []);

  return {
    history,
    recordTicker,
    refreshHistoryQuotes,
    clearHistory,
    isLoaded,
  };
};
