import React, { createContext, useContext, useReducer, useMemo, type ReactNode } from 'react';
import { merge } from 'es-toolkit';

export type Draft = {
  id?: string;
  title?: string;
  metaDescription?: string;
  lastUpdated?: number;
  bodyCopy?: string;
};

interface AppState {
  accountId: string;
  drupal: {
    entity: {
      type: string;
      id: number;
    };
    draftLinked: boolean;
  };
  keywords: string[];
  rankSource: {
    id?: number;
    description?: string;
    isoLocale?: string;
  };
  competitorUrls: string[];
  excludedCompetitorUrls: string[];
  brand: string;
  draft: Draft;
  draftUsage: {
    maxDrafts: number;
    usedDrafts: number;
    alertIsDismissed: boolean;
  };
  keywordsEvaluation: string;
}

const initialAppState: AppState = {
  accountId: '',
  drupal: {
    entity: {
      type: '',
      id: 0,
    },
    draftLinked: false,
  },
  keywords: [],
  rankSource: {},
  competitorUrls: [],
  excludedCompetitorUrls: [],
  brand: '',
  draft: {
    // @see DraftRepository::DEFAULT_DRAFT_TITLE
    title: 'Untitled Canvas draft',
  },
  draftUsage: {
    maxDrafts: 15,
    usedDrafts: 0,
    alertIsDismissed: false,
  },
  keywordsEvaluation: '',
};

type AppStateAction =
  | { type: 'setKeywords'; keywords: string[] }
  | { type: 'setRankSource'; rankSource: { id?: number; description?: string; isoLocale?: string } }
  | { type: 'setCompetitorUrls'; competitorUrls: string[] }
  | { type: 'setExcludedCompetitorUrls'; excludedCompetitorUrls: string[] }
  | { type: 'setBrand'; brand: string }
  | { type: 'updateDraft'; draft: Draft }
  | { type: 'setDrupalDraftLinked'; draftLinked: boolean }
  | { type: 'setDraftUsageAlertIsDismissed'; draftUsageAlertIsDismissed: boolean }
  | { type: 'setKeywordsEvaluation'; keywordsEvaluation: string };

type AppStateContextType = AppState | null;
type AppStateDispatchContextType = ((action: AppStateAction) => void) | null;

interface AppStateProviderProps {
  children: ReactNode;
  initialState?: Partial<AppState>; // Add this prop
}

const AppStateContext = createContext<AppStateContextType>(null);
const AppStateDispatchContext = createContext<AppStateDispatchContextType>(null);

export function AppStateProvider({ children, initialState }: AppStateProviderProps) {
  const mergedInitialState = merge(initialAppState, initialState ?? {});

  const [tasks, dispatch] = useReducer(appStateReducer, mergedInitialState);

  return (
    <AppStateContext.Provider value={tasks}>
      <AppStateDispatchContext.Provider value={dispatch}>
        {children}
      </AppStateDispatchContext.Provider>
    </AppStateContext.Provider>
  );
}

export function useAppState(): AppState {
  const context = useContext(AppStateContext);
  if (context === null) {
    throw new Error('useAppState must be used within a TasksProvider');
  }
  return context;
}

function appStateReducer(appState: AppState, action: AppStateAction): AppState {
  switch (action.type) {
    case 'setKeywords': {
      const fallbackTitle = `Untitled Canvas draft: ${action.keywords.join(', ')}`;
      const draftTitle =
        !appState.draft.title || appState.draft.title.startsWith('Untitled Canvas draft')
          ? fallbackTitle
          : appState.draft.title;
      return {
        ...appState,
        keywords: action.keywords,
        // Reset competitor URLs if keywords change.
        competitorUrls: [],
        excludedCompetitorUrls: [],
        draft: {
          ...appState.draft,
          title: draftTitle,
        },
      };
    }
    case 'setRankSource': {
      return {
        ...appState,
        rankSource: action.rankSource,
      };
    }
    case 'setCompetitorUrls': {
      return {
        ...appState,
        competitorUrls: action.competitorUrls,
      };
    }
    case 'setExcludedCompetitorUrls': {
      return {
        ...appState,
        excludedCompetitorUrls: action.excludedCompetitorUrls,
      };
    }
    case 'setBrand': {
      return {
        ...appState,
        brand: action.brand,
      };
    }
    case 'updateDraft': {
      return {
        ...appState,
        draft: {
          ...appState.draft,
          ...action.draft,
        },
      };
    }
    case 'setDrupalDraftLinked': {
      return {
        ...appState,
        drupal: {
          ...appState.drupal,
          draftLinked: action.draftLinked,
        },
      };
    }
    case 'setDraftUsageAlertIsDismissed': {
      return {
        ...appState,
        draftUsage: {
          ...appState.draftUsage,
          alertIsDismissed: action.draftUsageAlertIsDismissed,
        },
      };
    }
    case 'setKeywordsEvaluation': {
      return {
        ...appState,
        keywordsEvaluation: action.keywordsEvaluation,
      };
    }
    default: {
      throw Error('Unknown action: ' + action);
    }
  }
}

export function useAccountId(): string {
  const context = useContext(AppStateContext);
  if (context === null) {
    throw new Error('useAccountId must be used within an AppStateProvider');
  }
  return useMemo(() => context.accountId, [context.accountId]);
}

export function useDrupal(): { entity: { type: string; id: number | null }; draftLinked: boolean } {
  const context = useContext(AppStateContext);
  if (context === null) {
    throw new Error('useDrupal must be used within an AppStateProvider');
  }
  return useMemo(() => context.drupal, [context.drupal]);
}

export function useKeywords(): string[] {
  const context = useContext(AppStateContext);
  if (context === null) {
    throw new Error('useKeywords must be used within an AppStateProvider');
  }
  return useMemo(() => context.keywords, [context.keywords]);
}

export function useRankSource(): { id?: number; description?: string; isoLocale?: string } {
  const context = useContext(AppStateContext);
  if (context === null) {
    throw new Error('useRankSource must be used within an AppStateProvider');
  }
  return useMemo(() => context.rankSource, [context.rankSource]);
}

export function useCompetitorUrls(): string[] {
  const context = useContext(AppStateContext);
  if (context === null) {
    throw new Error('useCompetitorUrls must be used within an AppStateProvider');
  }
  return useMemo(() => context.competitorUrls, [context.competitorUrls]);
}

export function useExcludedCompetitorUrls(): string[] {
  const context = useContext(AppStateContext);
  if (context === null) {
    throw new Error('useExcludedCompetitorUrls must be used within an AppStateProvider');
  }
  return useMemo(() => context.excludedCompetitorUrls, [context.excludedCompetitorUrls]);
}

export function useBrand(): string {
  const context = useContext(AppStateContext);
  if (context === null) {
    throw new Error('useBrand must be used within an AppStateProvider');
  }
  return useMemo(() => context.brand, [context.brand]);
}

export function useDraft(): Draft {
  const context = useContext(AppStateContext);
  if (context === null) {
    throw new Error('useDraft must be used within an AppStateProvider');
  }
  return useMemo(() => context.draft, [context.draft]);
}

export function useDraftUsage(): {
  maxDrafts: number;
  usedDrafts: number;
  alertIsDismissed: boolean;
} {
  const context = useContext(AppStateContext);
  if (context === null) {
    throw new Error('useDraftUsage must be used within an AppStateProvider');
  }
  return useMemo(() => context.draftUsage ?? {}, [context.draftUsage]);
}

export function useKeywordsEvaluation(): string {
  const context = useContext(AppStateContext);
  if (context === null) {
    throw new Error('useKeywordsEvaluation must be used within an AppStateProvider');
  }
  return useMemo(() => context.keywordsEvaluation, [context.keywordsEvaluation]);
}

export function useAppStateDispatch(): (action: AppStateAction) => void {
  const context = useContext(AppStateDispatchContext);
  if (context === null) {
    throw new Error('useTasksDispatch must be used within a TasksProvider');
  }
  return context;
}
