import React, { ReactNode, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';

import { SuperBlocks } from '@freecodecamp/shared/config/curriculum';
import DropDown from '../../../assets/icons/dropdown';
import type { ChapterBasedSuperBlockStructure } from '../../../redux/prop-types';
import { ChapterIcon } from '../../../assets/chapter-icon';
import { type Chapter } from '@freecodecamp/shared/config/chapters';
import { Link } from '../../../components/helpers';
import { BlockLayouts, BlockLabel } from '@freecodecamp/shared/config/blocks';
import { FsdChapters } from '@freecodecamp/shared/config/chapters';
import { type Module } from '@freecodecamp/shared/config/modules';
import envData from '../../../../config/env.json';
import Block from './block';
import CheckMark from './check-mark';
import { default as BlockLabelComponent } from './block-label';

import './super-block-accordion.css';

const { showUpcomingChanges } = envData;

interface ChapterProps {
  dashedName: string;
  children: ReactNode;
  comingSoon?: boolean;
  isExpanded: boolean;
  totalSteps: number;
  completedSteps: number;
  superBlock: SuperBlocks;
  isLinkChapter?: boolean;
  examSlug?: string;
}

interface ModuleProps {
  dashedName: string;
  children: ReactNode;
  isExpanded: boolean;
  totalSteps: number;
  completedSteps: number;
  superBlock: SuperBlocks;
  comingSoon: boolean;
}

interface Challenge {
  id: string;
  block: string;
  blockLabel?: BlockLabel;
  title: string;
  fields: { slug: string };
  dashedName: string;
  challengeType: number;
  blockLayout: BlockLayouts;
  superBlock: SuperBlocks;
}

interface PopulatedBlock {
  name: string;
  blockLabel: BlockLabel | null;
  challenges: Challenge[];
}

interface PopulatedModule {
  name: string;
  comingSoon?: boolean;
  moduleType?: Module['moduleType'];
  blocks: PopulatedBlock[];
}

interface PopulatedChapter {
  name: string;
  comingSoon?: boolean;
  modules: PopulatedModule[];
}

interface SuperBlockAccordionProps {
  challenges: Challenge[];
  superBlock: SuperBlocks;
  structure: ChapterBasedSuperBlockStructure;
  chosenBlock: string;
  completedChallengeIds: string[];
  /**
   * When true, expands all chapters and modules and hides those with no matching challenges.
   * Used during search/filter.
   */
  expandAll?: boolean;
}

const Chapter = ({
  dashedName,
  children,
  isExpanded,
  comingSoon,
  totalSteps,
  completedSteps,
  superBlock,
  isLinkChapter,
  examSlug
}: ChapterProps) => {
  const { t } = useTranslation();
  const [open, setOpen] = useState(isExpanded);

  useEffect(() => {
    setOpen(isExpanded);
  }, [isExpanded]);

  const panelId = `chapter-panel-${dashedName}`;

  const isComplete = completedSteps === totalSteps && totalSteps > 0;
  const chapterLabel = t(`intro:${superBlock}.chapters.${dashedName}`);

  const chapterButtonContent = (
    <>
      <div className='chapter-button-left'>
        <span className='checkmark-wrap chapter-checkmark-wrap'>
          <CheckMark isCompleted={isComplete} />
        </span>
        <ChapterIcon className='map-icon' chapter={dashedName as FsdChapters} />
        {chapterLabel}
        {isLinkChapter && examSlug && (
          <BlockLabelComponent blockLabel={BlockLabel.exam} />
        )}
      </div>
      <div className='chapter-button-right'>
        {!comingSoon && !isLinkChapter && (
          <span className='chapter-steps'>
            {t('learn.steps-completed', {
              totalSteps,
              completedSteps
            })}
          </span>
        )}
        <span className='dropdown-wrap'>{!isLinkChapter && <DropDown />}</span>
      </div>
    </>
  );

  if (isLinkChapter && examSlug) {
    return (
      <li className='chapter'>
        <Link
          className='chapter-button'
          data-playwright-test-label='chapter-button'
          to={examSlug}
        >
          {chapterButtonContent}
        </Link>
      </li>
    );
  }

  return (
    <li className='chapter'>
      <button
        aria-controls={panelId}
        aria-expanded={open}
        className='chapter-button'
        data-playwright-test-label='chapter-button'
        onClick={() => setOpen(o => !o)}
        type='button'
      >
        {chapterButtonContent}
      </button>
      {open && (
        <ul className='chapter-panel' id={panelId}>
          {children}
        </ul>
      )}
    </li>
  );
};

const Module = ({
  dashedName,
  children,
  isExpanded,
  totalSteps,
  completedSteps,
  superBlock,
  comingSoon
}: ModuleProps) => {
  const { t } = useTranslation();
  const isComplete = totalSteps === 0 ? false : completedSteps === totalSteps;
  const { note, intro } = t(`intro:${superBlock}.module-intros.${dashedName}`, {
    returnObjects: true
  }) as {
    note: string;
    intro: string[];
  };

  const showModuleContent = !(comingSoon && !showUpcomingChanges);

  const [open, setOpen] = useState(isExpanded);

  useEffect(() => {
    setOpen(isExpanded);
  }, [isExpanded]);

  const panelId = `module-panel-${dashedName}`;

  return (
    <li>
      <button
        aria-controls={panelId}
        aria-expanded={open}
        className='module-button'
        onClick={() => setOpen(o => !o)}
        type='button'
      >
        <div className='module-button-left'>
          <span className='dropdown-wrap'>
            <DropDown />
          </span>
          <span className='checkmark-wrap'>
            <CheckMark isCompleted={isComplete} />
          </span>
          {t(`intro:${superBlock}.modules.${dashedName}`)}
        </div>
        <div className='module-button-right' data-testid='module-button-right'>
          {!comingSoon && !!totalSteps && (
            <span className='module-steps'>
              {t('learn.steps-completed', {
                totalSteps,
                completedSteps
              })}
            </span>
          )}
        </div>
      </button>
      {open && (
        <ul className='module-panel' id={panelId}>
          {comingSoon && (
            <div className='module-intro'>
              {note && (
                <p>
                  <b>{note}</b>
                </p>
              )}
              {intro?.length && intro.map(ntro => <p key={ntro}>{ntro}</p>)}
            </div>
          )}
          {showModuleContent && children}
        </ul>
      )}
    </li>
  );
};

const LinkModule = ({
  superBlock,
  challenges,
  accordion,
  moduleType,
  expandAll
}: {
  superBlock: SuperBlocks;
  challenges?: Challenge[];
  accordion: boolean;
  moduleType?: Module['moduleType'];
  expandAll?: boolean;
}) => {
  if (!challenges?.length) return null;

  const label = moduleType ?? challenges[0].blockLabel;

  return (
    <li className='link-block'>
      <Block
        block={challenges[0].block}
        blockLabel={label || null}
        challenges={challenges}
        superBlock={superBlock}
        accordion={accordion}
        expandAll={expandAll}
      />
    </li>
  );
};

export const SuperBlockAccordion = ({
  challenges,
  superBlock,
  structure,
  chosenBlock,
  completedChallengeIds,
  expandAll = false
}: SuperBlockAccordionProps) => {
  const superBlockStructure = structure;

  const modules = superBlockStructure.chapters.flatMap<Module>(
    ({ modules }) => modules
  );

  const isLinkModule = (name: string) => {
    const module = modules.find(module => module.dashedName === name);

    return (
      module?.moduleType === BlockLabel.review ||
      module?.moduleType === BlockLabel.exam ||
      module?.moduleType === BlockLabel.quiz ||
      module?.moduleType === BlockLabel.certProject
    );
  };

  const getBlockToChapterMap = () => {
    const blockToChapterMap = new Map<string, string>();
    superBlockStructure.chapters.forEach(chapter => {
      chapter.modules.forEach(module => {
        module.blocks.forEach(block => {
          blockToChapterMap.set(block, chapter.dashedName);
        });
      });
    });

    return blockToChapterMap;
  };

  const getBlockToModuleMap = () => {
    const blockToModuleMap = new Map<string, string>();
    modules.forEach(module => {
      module.blocks.forEach(block => {
        blockToModuleMap.set(block, module.dashedName);
      });
    });

    return blockToModuleMap;
  };

  const blockToChapterMap = getBlockToChapterMap();
  const blockToModuleMap = getBlockToModuleMap();
  const allChapters = useMemo<PopulatedChapter[]>(() => {
    const populateBlocks = (blocks: string[]): PopulatedBlock[] =>
      blocks.map(block => {
        const blockChallenges = challenges.filter(
          ({ block: blockName }) => blockName === block
        );

        return {
          name: block,
          blockLabel: blockChallenges[0]?.blockLabel ?? null,
          challenges: blockChallenges
        };
      });

    return superBlockStructure.chapters.map((chapter: Chapter) => ({
      name: chapter.dashedName,
      comingSoon: chapter.comingSoon,
      modules: chapter.modules.map((module: Module) => ({
        name: module.dashedName,
        comingSoon: module.comingSoon,
        moduleType: module.moduleType,
        blocks: populateBlocks(module.blocks)
      }))
    }));
  }, [challenges, superBlockStructure.chapters]);

  // Expand the outer layers in order to reveal the chosen block.
  const expandedChapter = blockToChapterMap.get(chosenBlock);
  const expandedModule = blockToModuleMap.get(chosenBlock);
  const accordion = true;

  return (
    <ul className='super-block-accordion'>
      {allChapters.map(chapter => {
        const chapterStepIds: string[] = [];
        chapter.modules.forEach(module => {
          const { blocks } = module;
          blocks.forEach(block =>
            chapterStepIds.push(...block.challenges.map(c => c.id))
          );
        });

        if (expandAll && chapterStepIds.length === 0) return null;

        const chapterStepIdsSet = new Set(chapterStepIds);

        const completedStepsInChapter = new Set(
          completedChallengeIds.filter(id => chapterStepIdsSet.has(id))
        ).size;

        const [firstChapterModule] = chapter.modules;

        const [firstModuleBlock] = firstChapterModule?.blocks ?? [];

        const isLinkChapter =
          chapter.modules.length === 1 &&
          firstChapterModule?.blocks.length === 1 &&
          firstModuleBlock?.blockLabel === BlockLabel.exam &&
          firstModuleBlock.challenges.length === 1;

        const examSlug = isLinkChapter
          ? firstModuleBlock?.challenges[0]?.fields.slug
          : undefined;

        return (
          <Chapter
            key={chapter.name}
            dashedName={chapter.name}
            isExpanded={
              expandAll ||
              expandedChapter === chapter.name ||
              allChapters.length === 1
            }
            comingSoon={chapter.comingSoon}
            totalSteps={chapterStepIds.length}
            completedSteps={completedStepsInChapter}
            superBlock={superBlock}
            isLinkChapter={isLinkChapter}
            examSlug={examSlug}
          >
            {chapter.modules.map(module => {
              if (module.comingSoon && !showUpcomingChanges) {
                if (module.moduleType === BlockLabel.review) {
                  return null;
                }
              }

              if (isLinkModule(module.name)) {
                return (
                  <LinkModule
                    key={module.name}
                    superBlock={superBlock}
                    moduleType={module.moduleType}
                    challenges={module.blocks[0]?.challenges}
                    accordion={accordion}
                    expandAll={expandAll}
                  />
                );
              }

              const moduleStepIds: string[] = [];
              module.blocks.forEach(block =>
                moduleStepIds.push(...block.challenges.map(c => c.id))
              );

              if (expandAll && moduleStepIds.length === 0) return null;

              const moduleStepIdsSet = new Set(moduleStepIds);
              const completedStepsInModule = new Set(
                completedChallengeIds.filter(id => moduleStepIdsSet.has(id))
              ).size;

              return (
                <Module
                  key={module.name}
                  dashedName={module.name}
                  isExpanded={expandAll || expandedModule === module.name}
                  totalSteps={moduleStepIds.length}
                  completedSteps={completedStepsInModule}
                  superBlock={superBlock}
                  comingSoon={!!module.comingSoon}
                >
                  {module.blocks.map(block => (
                    // maybe TODO: allow blocks to be "coming soon"
                    <li key={block.name}>
                      <Block
                        block={block.name}
                        blockLabel={block.blockLabel}
                        challenges={block.challenges}
                        superBlock={superBlock}
                        accordion={accordion}
                        expandAll={expandAll}
                      />
                    </li>
                  ))}
                </Module>
              );
            })}
          </Chapter>
        );
      })}
    </ul>
  );
};
