import { memo, useEffect } from 'react';
import classNames from 'classnames';
import Debug from 'debug';

import type { ReactNode } from 'react';
import type { ItemPageProps } from '..';
import type { PageSectionComponent } from '../sections/types';
import type { AddSectionDialogBoxProps } from './components/AddSectionButton/AddSectionDialog';

import useHash from '~/src/hooks/useHash';
import { useAppRouter } from '~/src/lib/router2';
import { TrackerProvider } from '~/src/lib/tracker/useTracker';
import { setLayoutSectionsAction } from '~/src/store/editStage/actions';
import { useDispatch, useSelector } from '~/src/store/redux';
import { selectUserIsLoading } from '~/src/store/session/selectors';
import Box from '../../Box';
import PageError from '../../PageError';
import PageLoading from '../../PageLoading';
import { PageSections } from '../components/SectionRenderer';
import { PageThemeProvider } from '../hooks/theme';
import { ItemPageContext } from '../ItemPageContext';
import LayoutSettingsDialog from '../layouts/components/LayoutSettingsDialog';
import ItemPageLayoutEdit from '../layouts/ItemPageLayoutEdit';
import { LayoutSlotIds } from '../types';
import AddSectionButton from './components/AddSectionButton';
import AddSectionDialogBox from './components/AddSectionButton/AddSectionDialog';
import { LAYOUT_SETTINGS_HASH_PARAM } from './components/ItemPageEditHeader';
import {
  PageSectionSortableContainer,
  PageSectionSortableItem,
} from './components/PageSectionSortableContainer';
import { reorderLayoutSections } from './reorderSections';

const debug = Debug('songwhip/components/ItemPageEdit');
const ADD_ITEM_HASH_PARAM = 'add';

interface ItemPageEditProps
  extends Omit<
    ItemPageProps,
    'isOwned' | 'pageOwnedByAccountIds' | 'toActionItems' | 'content'
  > {
  getAddSectionItems: AddSectionDialogBoxProps['getItems'];
  userCanEdit: boolean;
  headerContent?: ReactNode;
  sectionComponents: Record<string, PageSectionComponent>;
  children?: ReactNode;
}

/**
 * The edit code is probably the most complex part of the app.
 *
 * Overview:
 *
 * - <ItemPage{Artist|Album|Track}Edit> all wrap <ItemPageEdit>
 * - They fetch the respective Artist/AlbumTrack item
 * - They 'stage' a clone of this item in the `editStage` part of the redux store,
 *   this cloned object is where all edits are persisted/staged until the user hits 'Save'.
 * - The compos an ItemContext using the cloned Item and also define the optional `originalItemContext`
 *   which acts as a reference to the original item, we use it for diffing changes on save.
 * - When the user edits the page we fire redux actions to mutate the editStage Item, these
 *   changes immediately update in the view.
 * - When the user saves we identify if anything changed using the itemContext.originalItemContext
 *   and compose an ItemConfig that is persisted using songwhip-api.
 */
const ItemPageEditInner = memo<ItemPageEditProps>(
  ({
    isLoading,
    error,
    pagePath,
    hasContent,
    itemContext,
    getAddSectionItems: getAddFeatureListItems,
    userCanEdit,
    pageType,
    children,
    sectionComponents,
    testId,
    ...itemPageLayoutProps
  }) => {
    const router = useAppRouter();
    const { hasHashParam, backToBeforeFirstHash } = useHash();
    const userIsLoading = useSelector(selectUserIsLoading);
    const backgroundShouldRender = hasContent;
    const isReady = !isLoading || hasContent;
    const dispatch = useDispatch();

    debug('render', {
      itemContext,
      backgroundShouldRender,
    });

    // redirect back to main page if user doesn't have access
    useEffect(() => {
      if (!userIsLoading && !userCanEdit) {
        debug('!userCanEdit: redirecting', userCanEdit);
        router.replace(pagePath);
      }
    }, [userIsLoading, userCanEdit]);

    // only show loading spinner if the don't have any content to show
    if (!isReady) {
      return <PageLoading />;
    }

    // only show the error if we don't have any useful content to show
    if (error && !hasContent) {
      return <PageError error={error} />;
    }

    return (
      <>
        <ItemPageLayoutEdit
          settings={itemContext.layout.settings}
          testId={classNames('itemPageEdit', testId)}
          content={
            <Box padding="0 1.2rem 0">
              <PageSectionSortableContainer
                onChange={({ addedIndex, removedIndex }) => {
                  const pageSectionsNext = reorderLayoutSections({
                    sections: itemContext.layout.main,
                    itemContext,
                    components: sectionComponents,
                    removedIndex,
                    addedIndex,
                  });

                  debug('layout items order change', pageSectionsNext);

                  dispatch(
                    setLayoutSectionsAction({
                      sections: pageSectionsNext,
                      layoutSlotId: LayoutSlotIds.MAIN,
                    })
                  );
                }}
                render={() => {
                  return (
                    <PageSections
                      items={itemContext.layout.main}
                      components={sectionComponents}
                      itemContext={itemContext}
                      id={LayoutSlotIds.MAIN}
                      withTopMargin={false}
                      renderItem={({ component, index, isGrouped }) => {
                        return (
                          <PageSectionSortableItem
                            key={index}
                            style={{
                              paddingTop: index
                                ? !isGrouped
                                  ? '2rem'
                                  : '.8rem'
                                : '',
                            }}
                          >
                            {component}
                          </PageSectionSortableItem>
                        );
                      }}
                    />
                  );
                }}
              />
              <AddSectionButton
                margin="2rem 0 0"
                getAddFeatureListItems={getAddFeatureListItems}
              />
            </Box>
          }
          {...itemPageLayoutProps}
        />
        {children}
        {hasHashParam(ADD_ITEM_HASH_PARAM) && (
          <AddSectionDialogBox
            onClose={backToBeforeFirstHash}
            getItems={getAddFeatureListItems}
          />
        )}
        {hasHashParam(LAYOUT_SETTINGS_HASH_PARAM) && (
          <LayoutSettingsDialog onClose={backToBeforeFirstHash} />
        )}
      </>
    );
  }
);

// Because <ItemPageEditInner> uses useOnSave() which uses useTracker()
// we must make sure that the TrackerProvider is an ancestor.
const ItemPageEdit = memo<ItemPageEditProps>(({ trackerContext, ...props }) => {
  const { itemContext } = props;

  return (
    <TrackerProvider baseContext={trackerContext}>
      <ItemPageContext.Provider value={itemContext}>
        <PageThemeProvider itemContext={itemContext}>
          <ItemPageEditInner {...props} />
        </PageThemeProvider>
      </ItemPageContext.Provider>
    </TrackerProvider>
  );
});

export default ItemPageEdit;
