import React, { useState, useEffect } from 'react';
import Typography from '@mui/material/Typography';
import { loadFromLocalStorage } from '@laboratoria/sdk-js';
import { useApp } from '../app';
import Content from '../Content';
import Loading from '../Loading';
import CodeInput from './CodeInput';
import FormInput from './FormInput';
import createTestRunner from './test-runner';

const camelCased = str => str.replace(/-([a-z])/g, g => g[1].toUpperCase());

const challengeToFilename = ({ slug, env }) => {
  const slugWithoutPrefix = slug.replace(/^\d{2}-/, '');

  return (
    env === 'dom'
      ? `${slugWithoutPrefix}.html`
      : `${camelCased(slugWithoutPrefix)}.js`
  );
};

const parseState = (data) => {
  const { testResults, ...rest } = data;

  return {
    ...rest,
    testResults: (
      testResults && testResults.type === 'error'
        ? new Error(testResults.message)
        : testResults
    ),
  };
};

const stateToJSON = ({ editorValue, testResults }) => ({
  editorValue,
  testResults: (
    testResults instanceof Error
      ? { type: 'error', message: testResults.message }
      : testResults
  ),
});

const Challenge = ({ challenge, lang, pathPrefix, cohortId }) => {
  const app = useApp();
  const { user } = app.auth;
  const fname = challengeToFilename(challenge);
  const boilerplate = challenge.files[`/boilerplate/${fname}`];
  const path = `${pathPrefix ? `${pathPrefix}/` : ''}${challenge.slug}/${challenge.version}`;

  const cachedInLocalStorage = loadFromLocalStorage(path);
  const [editorValue, setEditorValue] = useState(
    cachedInLocalStorage?.editorValue || boilerplate || '',
  );
  const [testResults, setTestResults] = useState(cachedInLocalStorage?.testResults);
  const runTests = createTestRunner(challenge, lang, fname);
  const localPersistence = !user || pathPrefix !== 'application';

  const runAndSave = async () => {
    try {
      const testResults = await runTests(editorValue);
      const data = stateToJSON({ editorValue, testResults });

      if (localPersistence) {
        window.localStorage.setItem(path, JSON.stringify(data));
        return setTestResults(parseState(data).testResults);
      }

      const activityLogEntry = await app.activityLogEntry.create({
        data: {
          user: { connect: { uid: user.uid } },
          type: 'challenge',
          data,
          path,
          // TODO: Cohort es opcional!
          cohort: { connect: { id: cohortId } },
        },
      });

      setTestResults(parseState(activityLogEntry.data).testResults);
    } catch (err) {
      setTestResults(err);
    }
  };

  useEffect(() => {
    if (localPersistence) {
      if (!testResults) {
        setTestResults(null);
      }
      return;
    }
    app.activityLogEntry.findMany({
      where: {
        uid: user.uid,
        type: 'challenge',
        path,
        // TODO: Cohort es opcional!
        cohortId,
      },
      orderBy: { updatedAt: 'desc' },
      take: 1,
    })
      .then(([entry]) => {
        if (!entry) {
          setTestResults(null);
          return;
        }
        const { editorValue, testResults } = parseState(entry.data);
        setEditorValue(editorValue);
        setTestResults(testResults);
      })
      .catch(console.error);
  }, []);

  if (typeof testResults === 'undefined') {
    return <Loading />;
  }

  const Input = challenge.env === 'form' ? FormInput : CodeInput;

  return (
    <>
      <Typography variant="h1">{challenge.intl[lang].title}</Typography>
      <Content html={challenge.intl[lang].body} />
      <Input
        challenge={challenge}
        lang={lang}
        editorValue={editorValue}
        setEditorValue={setEditorValue}
        runAndSave={runAndSave}
        testResults={testResults}
      />
    </>
  );
};

export default Challenge;
