import {
  startTransition,
  useDeferredValue,
  useEffect,
  useEffectEvent,
  useReducer,
} from 'react';
import { newsBridge } from '../lib/newsBridge';
import type {
  NewsArticle,
  NewsSourceStatus,
  QueryNewsFeedRequest,
  RefreshNewsFeedResult,
} from '../types/news';

export interface NewsFeedState {
  articles: NewsArticle[];
  total: number;
  lastSyncedAt?: string;
  sources: NewsSourceStatus[];
  filters: QueryNewsFeedRequest;
  isLoading: boolean;
  isRefreshing: boolean;
  error?: string;
}

type NewsFeedAction =
  | { type: 'load_started'; refresh: boolean }
  | {
      type: 'load_succeeded';
      articles: NewsArticle[];
      total: number;
      lastSyncedAt?: string;
      sources: NewsSourceStatus[];
    }
  | { type: 'load_failed'; error: string }
  | { type: 'filters_merged'; filters: QueryNewsFeedRequest }
  | { type: 'article_updated'; articleId: string; patch: Partial<NewsArticle> };

export const createNewsFeedState = (
  filters: QueryNewsFeedRequest = {},
): NewsFeedState => ({
  articles: [],
  total: 0,
  lastSyncedAt: undefined,
  sources: [],
  filters,
  isLoading: true,
  isRefreshing: false,
  error: undefined,
});

export const newsFeedReducer = (
  state: NewsFeedState,
  action: NewsFeedAction,
): NewsFeedState => {
  switch (action.type) {
    case 'load_started':
      return {
        ...state,
        isLoading: !action.refresh,
        isRefreshing: action.refresh,
        error: undefined,
      };
    case 'load_succeeded':
      return {
        ...state,
        articles: action.articles,
        total: action.total,
        lastSyncedAt: action.lastSyncedAt,
        sources: action.sources,
        isLoading: false,
        isRefreshing: false,
        error: undefined,
      };
    case 'load_failed':
      return {
        ...state,
        isLoading: false,
        isRefreshing: false,
        error: action.error,
      };
    case 'filters_merged':
      return {
        ...state,
        filters: {
          ...state.filters,
          ...action.filters,
          offset: action.filters.offset ?? state.filters.offset ?? 0,
        },
      };
    case 'article_updated':
      return {
        ...state,
        articles: state.articles.map((article) =>
          article.id === action.articleId ? { ...article, ...action.patch } : article,
        ),
      };
    default:
      return state;
  }
};

export const useNewsFeed = (initialFilters: QueryNewsFeedRequest = {}) => {
  const [state, dispatch] = useReducer(
    newsFeedReducer,
    initialFilters,
    createNewsFeedState,
  );
  const deferredSearch = useDeferredValue(state.filters.search);
  const deferredFilters = {
    ...state.filters,
    search: deferredSearch,
  };

  const loadFeed = useEffectEvent(async (refresh: boolean) => {
    dispatch({ type: 'load_started', refresh });
    try {
      const response = await newsBridge.queryNewsFeed(deferredFilters);
      dispatch({
        type: 'load_succeeded',
        articles: response.articles,
        total: response.total,
        lastSyncedAt: response.lastSyncedAt,
        sources: response.sources,
      });
    } catch (error) {
      dispatch({
        type: 'load_failed',
        error: error instanceof Error ? error.message : String(error),
      });
    }
  });

  useEffect(() => {
    void loadFeed(false);
  }, [
    deferredFilters.limit,
    deferredFilters.offset,
    deferredFilters.onlyHighlighted,
    deferredFilters.onlySaved,
    deferredFilters.onlyUnread,
    deferredFilters.search,
    deferredFilters.ticker,
    loadFeed,
  ]);

  useEffect(() => {
    let disposed = false;
    let unlisten: (() => void) | undefined;

    void newsBridge.listenForUpdates(() => {
      if (!disposed) {
        void loadFeed(false);
      }
    }).then((listener) => {
      unlisten = listener;
    });

    return () => {
      disposed = true;
      unlisten?.();
    };
  }, [loadFeed]);

  const setFilters = (filters: QueryNewsFeedRequest) => {
    startTransition(() => {
      dispatch({ type: 'filters_merged', filters });
    });
  };

  const refresh = async (force = false): Promise<RefreshNewsFeedResult> => {
    dispatch({ type: 'load_started', refresh: true });
    try {
      const result = await newsBridge.refreshNewsFeed({ force });
      await loadFeed(true);
      return result;
    } catch (error) {
      dispatch({
        type: 'load_failed',
        error: error instanceof Error ? error.message : String(error),
      });
      throw error;
    }
  };

  const toggleSaved = async (articleId: string, isSaved: boolean) => {
    dispatch({ type: 'article_updated', articleId, patch: { isSaved } });
    try {
      await newsBridge.updateNewsArticleState({ articleId, isSaved });
    } catch (error) {
      await loadFeed(false);
      throw error;
    }
  };

  const markRead = async (articleId: string, isRead = true) => {
    dispatch({ type: 'article_updated', articleId, patch: { isRead } });
    try {
      await newsBridge.updateNewsArticleState({ articleId, isRead });
    } catch (error) {
      await loadFeed(false);
      throw error;
    }
  };

  return {
    ...state,
    refresh,
    setFilters,
    toggleSaved,
    markRead,
  };
};
