/**
 *  Copyright (C) 2018 Basalt
 This file is part of Knapsack.
 Knapsack is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the Free
 Software Foundation; either version 2 of the License, or (at your option)
 any later version.

 Knapsack is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along
 with Knapsack; if not, see <https://www.gnu.org/licenses>.
 */

import React, { useState, useRef, Suspense, lazy } from 'react';
import { CodeBlock, CircleSpinner } from '@knapsack/design-system';
import { useHistory } from 'react-router-dom';
import {
  updateTemplateInfo,
  useDispatch,
  useSelector,
  updateTemplateDemo,
} from '../../store';
import MdBlock from '../../components/md-block';
// import DosAndDonts from '../../components/dos-and-donts';
import './template-view.scss';
import './shared/demo-grid-controls.scss';
import {
  DataDemo,
  isDataDemo,
  isTemplateDemo,
} from '../../../schemas/patterns';
import { BASE_PATHS } from '../../../lib/constants';
import { formatHtmlCode } from '../../../lib/util/prettier-format';
import {
  CurrentTemplateContext,
  CurrentTemplateData,
} from './current-template-context';
import { TemplateHeader, KsDemoStage } from './components';
import { KsTemplateDemos } from './components/template-demos';

const KsSpecDocs = lazy(() => import('./components/spec-docs'));

export type Props = {
  /**
   * Pattern ID
   */
  id: string;
  templateId: string;
  demoId?: string;
  isVerbose?: boolean;
  /**
   * @todo remove `string` - it's just to make Typescript happy
   */
  demoSize?: 's' | 'm' | 'l' | 'full' | string;
  isReadmeShown?: boolean;
  isTitleShown?: boolean;
  isSchemaFormShown?: boolean;
  isCodeBlockShown?: boolean;
};

const TemplateView: React.FC<Props> = ({
  isVerbose = true,
  demoSize,
  isReadmeShown = true,
  isTitleShown = true,
  isSchemaFormShown = true,
  isCodeBlockShown = false,
  id,
  templateId,
  demoId,
}: Props) => {
  const patternId = id;
  const permissions = useSelector(store => store.userState.role.permissions);
  const canEdit = useSelector(store => store.userState.canEdit);
  const isLocalDev = useSelector(store => store.userState.isLocalDev);
  const allPatterns = useSelector(({ patternsState }) =>
    Object.values(patternsState.patterns),
  );
  const pattern = useSelector(
    ({ patternsState }) => patternsState.patterns[id],
  );
  if (!pattern) {
    const availablePatternIds = allPatterns.map(p => p.id).join(', ');
    const msg = `The pattern "${id}" was not found, these are the available ids: "${availablePatternIds}".`;
    console.error(msg);
    throw new Error(msg);
  }
  const history = useHistory();
  const allStatuses = useSelector(s => s.patternsState.templateStatuses);
  const { allAssetSets, globalAssetSetIds } = useSelector(
    ({ assetSetsState }) => ({
      allAssetSets: assetSetsState.allAssetSets,
      globalAssetSetIds: assetSetsState.globalAssetSetIds,
    }),
  );
  const [renderedHtml, setRenderedHtml] = useState('');
  const [codeUsage, setCodeUsage] = useState<{
    code: string;
    language: string;
  }>();

  const dispatch = useDispatch();

  const { templates } = pattern;

  const template = templates.find(t => t.id === templateId);

  const {
    spec = {},
    title,
    assetSetIds: templateAssetSetIds,
    demosById,
    statusId,
  } = template;
  const originalDemos = useRef(demosById); // used to reset demo

  const { props: schema } = spec;
  const status = allStatuses.find(p => p.id === statusId);
  const hasSchema = !!(Object.keys(schema?.properties || {}).length > 0);
  const readme = ''; // @todo

  const backupDemo: DataDemo = {
    type: 'data',
    id: 'blank',
    description: '',
    title: 'Blank',
    data: {
      props: {},
      slots: {},
    },
  };

  const demos = template?.demos?.map(d => demosById[d]) ?? [];
  const [firstDemo] = demos;

  const demo =
    demosById && demoId ? demosById[demoId] : firstDemo ?? backupDemo;

  if (demoId && !demosById[demoId]) {
    history.replace(`${BASE_PATHS.PATTERN}/${patternId}/${templateId}`);
  }

  const isCodeBlockEditable = demo && isTemplateDemo(demo) && isLocalDev;
  const assetSetIds = templateAssetSetIds ?? globalAssetSetIds;
  const assetSets = assetSetIds.map(assetSetId => ({
    id: assetSetId,
    ...allAssetSets[assetSetId],
  }));
  const [assetSetId, setAssetSetId] = useState(
    assetSets[0] ? assetSets[0].id : '',
  );
  const showSchemaForm = isSchemaFormShown && hasSchema;

  const currentTemplateData: CurrentTemplateData = {
    patternId,
    pattern,
    templateId,
    template,
    demo,
    demos,
    spec,
    canEdit,
    isLocalDev,
    title,
    hasSchema,
    assetSetId,
  };

  const codeBlock = (
    <div className="ks-template-view__code-block-wrapper">
      <CodeBlock
        items={[
          codeUsage?.code
            ? {
                id: 'usage',
                name: 'Usage',
                code: codeUsage.code,
                language: codeUsage.language,
                // isEditable: isCodeBlockEditable,
                isEditable: false,
                // handleChange: newCode => {
                //   // socket.send(
                //   //   JSON.stringify({
                //   //     patternId,
                //   //     templateId,
                //   //     demoId: demo.id,
                //   //     code: newCode,
                //   //   }),
                //   // );
                //   window
                //     .fetch('/api/tmp-save', {
                //       method: 'POST',
                //       headers: {
                //         'Content-Type': 'application/json',
                //         Accept: 'application/json',
                //       },
                //       body: JSON.stringify({
                //         patternId,
                //         templateId,
                //         demoId: demo.id,
                //         code: newCode,
                //       }),
                //     })
                //     .then(res => res.json())
                //     .then(x => {
                //       console.log(x);
                //     });
                //   // console.log('new code', newCode);
                // },
              }
            : null,
          // {
          //   id: 'templateSrc',
          //   name: 'Template Source',
          // },
          renderedHtml
            ? {
                id: 'html',
                name: 'HTML',
                language: 'html',
                code: formatHtmlCode(renderedHtml),
              }
            : null,
        ].filter(Boolean)}
      />
    </div>
  );

  return (
    <CurrentTemplateContext.Provider value={currentTemplateData}>
      <article className="ks-template-view">
        <div className="ks-template-view__overview-wrapper">
          {isVerbose && (
            <TemplateHeader
              assetSets={assetSets}
              status={status}
              isTitleShown={!isVerbose && isTitleShown}
              handleAssetSetChange={newAssetSet => {
                setAssetSetId(newAssetSet.value);
              }}
              handleStatusChange={newStatus => {
                dispatch(
                  updateTemplateInfo({
                    templateId,
                    patternId,
                    template: {
                      statusId: newStatus.value,
                    },
                  }),
                );
              }}
            />
          )}

          {isVerbose && <KsTemplateDemos />}

          <KsDemoStage
            demoSize={demoSize}
            isFormVisible={showSchemaForm && isDataDemo(demo)}
            key={demo?.id}
            handleCodeUsage={setCodeUsage}
            handleRenderedHtml={setRenderedHtml}
            handlePropsChange={props => {
              if (isDataDemo(demo)) {
                dispatch(
                  updateTemplateDemo({
                    patternId,
                    templateId,
                    demo: {
                      ...demo,
                      data: {
                        ...demo.data,
                        props,
                      },
                    },
                  }),
                );
              }
            }}
            handleSlotsChange={slotsData => {
              if (isDataDemo(demo)) {
                dispatch(
                  updateTemplateDemo({
                    patternId,
                    templateId,
                    demo: {
                      ...demo,
                      data: {
                        ...demo.data,
                        slots: slotsData,
                      },
                    },
                  }),
                );
              }
            }}
            handleDemoReset={() => {
              const originalDemo = originalDemos.current[demo?.id] ?? {
                ...demo,
                data: {
                  props: {},
                  slots: {},
                },
              };
              dispatch(
                updateTemplateDemo({
                  patternId,
                  templateId,
                  demo: originalDemo,
                }),
              );
            }}
          />
        </div>
        {isCodeBlockShown && codeBlock}
        {isReadmeShown && readme && (
          <MdBlock
            md={readme}
            key={`${id}-${templateId}`}
            isEditorShown={permissions.includes('write')}
            title="Documentation (not wired up to save right now)"
            handleChange={newReadme => {
              // @todo save it
              console.log('handleSave on readme called', newReadme);
            }}
          />
        )}
        {isVerbose && (
          <Suspense fallback={<CircleSpinner />}>
            <KsSpecDocs />
          </Suspense>
        )}
      </article>
    </CurrentTemplateContext.Provider>
  );
};

export default TemplateView;
