import { getGithubUrl, handleAppError } from '@learnitive/base-utils';
import { SERVICE_NAME } from '@learnitive/constants';
import { useCoreServices, useLogger } from '@learnitive/context';
import type { AppError, RootEmitter } from '@learnitive/types';
import { toast } from '@learnitive/ui-components';
import React, { useEffect } from 'react';

export function shouldReportAppError(appError: AppError): boolean {
  switch (appError.name) {
    case 'error::file:already-existing':
    case 'error::file:size-too-large':
    case 'error::file-storage:file-does-not-exist':
    case 'error::workspace:native-fs-auth-needed':
    case 'error::workspace:native-fs-locate-failed':
    case 'error::workspace:native-fs-reconnect-failed':
    case 'error::workspace:no-note-opened':
    case 'error::workspace:no-notes-found':
    case 'error::workspace:not-opened':
    case 'error::workspace:project-limit-reached':
    case 'error::ws-path:create-new-note':
    case 'error::ws-path:invalid-markdown-path':
    case 'error::ws-path:invalid-note-path':
    case 'error::ws-path:invalid-ws-name':
    case 'error::ws-path:invalid-ws-path':
      return false;
    default:
      return true;
  }
}

export function AppErrorHandler({ rootEmitter }: { rootEmitter: RootEmitter }) {
  const coreServices = useCoreServices();
  const logger = useLogger();

  useEffect(() => {
    const controller = new AbortController();
    const showUnexpectedError = (error: Error) => {
      toast.error(error.message, {
        duration: Number.POSITIVE_INFINITY,
        cancel: {
          label: t.app.common.dismiss,
          onClick: () => {},
        },
        action: {
          label: t.app.common.report,
          onClick: () => {
            window.open(getGithubUrl(error, logger), '_blank');
          },
        },
      });
    };

    const showAppLikeError = (error: Error) => {
      toast.error(error.message, {
        duration: 5000,
        cancel: {
          label: t.app.common.dismiss,
          onClick: () => {},
        },
        action: {
          label: t.app.common.report,
          onClick: () => {
            window.open(getGithubUrl(error, logger), '_blank');
          },
        },
      });
    };

    const handleAppLikeError = (error: Error) => {
      return handleAppError(error, (appError, error) => {
        if (shouldReportAppError(appError)) {
          logger.error(error);
        } else {
          logger.warn('Handled app error:', error.message);
        }

        switch (appError.name) {
          case 'error::editor:save-failed': {
            const toastId = `editor-save-failed:${appError.payload.wsPath}`;
            toast.error(t.app.toasts.saveFailed, {
              id: toastId,
              duration: Number.POSITIVE_INFINITY,
              action: {
                label: t.app.toasts.retrySave,
                onClick: () => {
                  if (
                    coreServices.editorEngine.retryFailedSave(
                      appError.payload.wsPath,
                    )
                  ) {
                    toast.dismiss(toastId);
                  }
                },
              },
            });
            return;
          }

          case 'error::workspace:native-fs-auth-needed': {
            coreServices.commandDispatcher.dispatch(
              'command::ui:native-fs-auth',
              { wsName: appError.payload.wsName },
              'AppErrorHandler',
            );
            return;
          }

          case 'error::workspace:native-fs-locate-failed':
          case 'error::workspace:native-fs-reconnect-failed':
          case 'error::workspace:project-limit-reached': {
            // Expected user-facing outcomes, not app defects: no Report action.
            toast.error(error.message, {
              duration: 5000,
              cancel: {
                label: t.app.common.dismiss,
                onClick: () => {},
              },
            });
            return;
          }

          case 'error::file-storage:file-does-not-exist': {
            if (
              appError.payload.storage ===
                SERVICE_NAME.fileStorageNativeFsService &&
              appError.payload.wsPath.endsWith(':')
            ) {
              // WorkspaceState exposes a missing Native FS root as a full-page
              // recovery view. A second transient toast would compete with it.
              return;
            }
            showAppLikeError(error);
            return;
          }

          case 'error::file:size-too-large': {
            return;
          }

          default: {
            showAppLikeError(error);
            return;
          }
        }
      });
    };

    rootEmitter.on(
      'event::error:uncaught-error',
      (event) => {
        if (!event.isAppError) {
          showUnexpectedError(event.error);
          return;
        }
        const handled = handleAppLikeError(event.error);
        if (!handled) {
          showUnexpectedError(event.error);
        }
      },
      controller.signal,
    );

    return () => {
      controller.abort();
    };
  }, [rootEmitter, coreServices, logger]);

  return null;
}
