const { isEmpty, padStart, replace, trimStart } = require('lodash');

const required = () => {
  return (value) => {
    return isEmpty(value) ? 'This field is required' : true;
  };
};

const validateConfirm = () => {
  return (value) => {
    return value !== 'y' || value !== 'N' ? '"y" or "N" only' : true;
  };
};

const sanitize = value => replace(value, ' ', '');

const withConcat = value => `with hoc${trimStart(
  padStart(value, value.length + 1),
  'withoc'
)}`;

module.exports = (plop) => {
  plop.setGenerator('page', {
    description: 'Generate a Page',
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'Enter name of Page:',
        validate: required(),
      },
      {
        type: 'input',
        name: 'element',
        message: 'What is the first HTML element of your page? Default:',
        default: 'main',
        filter: sanitize,
      },
      {
        type: 'confirm',
        name: 'wantBase',
        message: 'Do you want to generate base Page? Default: N',
        default: false,
        validate: validateConfirm(),
      },
    ],
    actions: ({ wantBase }) => {
      let actions = [
        {
          type: 'add',
          path: 'src/pages/{{pascalCase name}}/{{pascalCase name}}.scss',
          templateFile: 'plop-templates/pages/Page/Page.scss.hbs',
          skipIfExists: true,
        },
        {
          type: 'append',
          path: 'src/pages/index.ts',
          pattern: '/* INDEX_EXPORTS */',
          template: "export * from './{{pascalCase name}}';",
        },
        {
          type: 'append',
          path: 'src/index.tsx',
          pattern: '/* PAGE_IMPORTS */',
          template: '\t{{pascalCase name}},\n' +
            '\t{{pascalCase name}}WithHoc,',
        },
        {
          type: 'append',
          path: 'src/index.tsx',
          pattern: '<Routes>',
          template: "\t\t\t\t\t<Route path='/{{dashCase name}}' element={<{{pascalCase name}}WithHoc />} />",
        },
      ];

      if (wantBase) {
        actions = actions.concat([
          {
            type: 'add',
            path: 'src/pages/{{pascalCase name}}/index.tsx',
            templateFile: 'plop-templates/pages/Page/index-base.tsx.hbs',
            skipIfExists: true,
          },
        ]);

        return actions;
      }

      actions = actions.concat([
        {
          type: 'add',
          path: 'src/pages/{{pascalCase name}}/index.tsx',
          templateFile: 'plop-templates/pages/Page/index.tsx.hbs',
          skipIfExists: true,
        },
      ]);

      return actions;
    },
  });

  plop.setGenerator('component', {
    description: 'Generate a Component',
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'Enter name of Component:',
        validate: required(),
      },
      {
        type: 'input',
        name: 'element',
        message: 'What is the main HTML element wrapper? Default:',
        default: 'div',
        filter: sanitize,
      },
      {
        type: 'confirm',
        name: 'wantBase',
        message: 'Do you want to generate base Component? Default: N',
        default: false,
        validate: validateConfirm(),
      },
    ],
    actions: ({ wantBase }) => {
      let actions = [
        {
          type: 'add',
          path: 'src/components/{{pascalCase name}}/{{pascalCase name}}.scss',
          templateFile: 'plop-templates/components/Component/Component.scss.hbs',
          skipIfExists: true,
        },
        {
          type: 'append',
          path: 'src/components/index.ts',
          pattern: '/* INDEX_EXPORTS */',
          template: "export * from './{{pascalCase name}}';",
        },
      ];

      if (wantBase) {
        actions = actions.concat([
          {
            type: 'add',
            path: 'src/components/{{pascalCase name}}/index.tsx',
            templateFile: 'plop-templates/components/Component/index-base.tsx.hbs',
            skipIfExists: true,
          },
        ]);

        return actions;
      }

      actions = actions.concat([
        {
          type: 'add',
          path: 'src/components/{{pascalCase name}}/index.tsx',
          templateFile: 'plop-templates/components/Component/index.tsx.hbs',
          skipIfExists: true,
        },
      ]);

      return actions;
    },
  });

  plop.setGenerator('hoc', {
    description: 'Generate a Higher-Order Component',
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'Enter name of Higher-Order Component:',
        validate: required(),
        filter: withConcat,
      },
    ],
    actions: [
      {
        type: 'add',
        path: 'src/hoc/{{pascalCase name}}/index.tsx',
        templateFile: 'plop-templates/hoc/WithHoc/index.tsx.hbs',
        skipIfExists: true,
      },
      {
        type: 'append',
        path: 'src/hoc/index.ts',
        pattern: '/* INDEX_EXPORTS */',
        template: "export * from './{{pascalCase name}}';",
      },
    ],
  });

  plop.setGenerator('redux', {
    description: 'Generate a Redux Structure',
    prompts: [
      {
        type: 'input',
        name: 'model',
        message: 'Enter name of Model:',
        validate: required(),
      },
      {
        type: 'input',
        name: 'page',
        message: 'In what page you want to add Redux imports? Default:',
        default: 'app',
      },
      {
        type: 'confirm',
        name: 'wantImportsOnly',
        message: 'Do you want Redux imports only? Default: N',
        default: false,
        validate: validateConfirm(),
      },
      {
        type: 'confirm',
        name: 'wantFormikYup',
        message: 'Will you use Formik and Yup? Default: Y',
        default: true,
        validate: validateConfirm(),
      },
    ],
    actions: ({ wantImportsOnly, wantFormikYup }) => {
      let actions = [
        {
          type: 'append',
          path: 'src/pages/{{pascalCase page}}/index.tsx',
          pattern: "} from '../../hoc';",
          template: '\nimport {\n' +
            '\tcreate{{pascalCase model}},\n' +
            '\tdelete{{pascalCase model}},\n' +
            '\tgetAll{{pascalCase model}}s,\n' +
            '\tgetOne{{pascalCase model}},\n' +
            '\tupdate{{pascalCase model}},\n' +
            "} from '../../controllers';\n\n" +
            'import {\n' +
            '\tresetCreate{{pascalCase model}},\n'+
            '\tresetUpdate{{pascalCase model}},\n'+
            '\tselectAll{{pascalCase model}}s,\n' +
            '\tselectAll{{pascalCase model}}sError,\n' +
            '\tselectAll{{pascalCase model}}sStatus,\n' +
            '\tselectCreated{{pascalCase model}},\n' +
            '\tselectCreate{{pascalCase model}}Error,\n' +
            '\tselectCreate{{pascalCase model}}Status,\n' +
            '\tselectDeleted{{pascalCase model}},\n' +
            '\tselectDelete{{pascalCase model}}Error,\n' +
            '\tselectDelete{{pascalCase model}}Status,\n' +
            '\tselectOne{{pascalCase model}},\n' +
            '\tselectOne{{pascalCase model}}Error,\n' +
            '\tselectOne{{pascalCase model}}Status,\n' +
            '\tselectUpdated{{pascalCase model}},\n' +
            '\tselectUpdate{{pascalCase model}}Error,\n' +
            '\tselectUpdate{{pascalCase model}}Status,\n' +
            '\tsetCreate{{pascalCase model}},\n' +
            '\tsetUpdate{{pascalCase model}},\n' +
            "} from '../../store';",
          abortOnFail: true,
        },
        {
          type: 'append',
          path: 'src/pages/{{pascalCase page}}/index.tsx',
          pattern: 'const dispatch = useAppDispatch();',
          template: '\n\tuseEffect(() => {\n' +
            '\t\tdispatch(getAll{{pascalCase model}}s());\n' +
            '\t\tdispatch(getOne{{pascalCase model}}(1));\n' +
            '\t}, [dispatch]);\n\n' +
            '\tconst all{{pascalCase model}}s = useAppSelector(selectAll{{pascalCase model}}s);\n' +
            '\tconst all{{pascalCase model}}sStatus = useAppSelector(selectAll{{pascalCase model}}sStatus);\n' +
            '\tconst all{{pascalCase model}}sError = useAppSelector(selectAll{{pascalCase model}}sError);\n\n' +
            '\tconst handleGetOne{{pascalCase model}} = (id: number) => {\n' +
            '\t\tdispatch(getOne{{pascalCase model}}(id));\n' +
            '\t};\n\n' +
            '\tconst one{{pascalCase model}} = useAppSelector(selectOne{{pascalCase model}});\n' +
            '\tconst one{{pascalCase model}}Status = useAppSelector(selectOne{{pascalCase model}}Status);\n' +
            '\tconst one{{pascalCase model}}Error = useAppSelector(selectOne{{pascalCase model}}Error);\n\n' +
            '\tconst handleCreate{{pascalCase model}} = (e: any, values: any, resetForm: any) => {\n' +
            '\t\thandleFormDataChange(e, setCreate{{pascalCase model}});\n' +
            '\t\thandleFormikDataChange(values, setCreate{{pascalCase model}}, resetForm);\n' +
            '\t\tdispatch(create{{pascalCase model}}()).unwrap()\n' +
            '\t\t\t.then(() => {\n' +
            '\t\t\t\tdispatch(resetCreate{{pascalCase model}}());\n' +
            '\t\t\t});\n' +
            '\t};\n\n' +
            '\tconst created{{pascalCase model}} = useAppSelector(selectCreated{{pascalCase model}});\n' +
            '\tconst create{{pascalCase model}}Status = useAppSelector(selectCreate{{pascalCase model}}Status);\n' +
            '\tconst create{{pascalCase model}}Error = useAppSelector(selectCreate{{pascalCase model}}Error);\n\n' +
            '\tconst handleUpdate{{pascalCase model}} = (e: any, values: any, resetForm: any, id: number) => {\n' +
            '\t\thandleFormDataChange(e, setUpdate{{pascalCase model}});\n' +
            '\t\thandleFormikDataChange(values, setUpdate{{pascalCase model}}, resetForm);\n' +
            '\t\tdispatch(update{{pascalCase model}}(id)).unwrap()\n' +
            '\t\t\t.then(() => {\n' +
            '\t\t\t\tdispatch(resetUpdate{{pascalCase model}}());\n' +
            '\t\t\t});\n' +
            '\t};\n\n' +
            '\tconst updated{{pascalCase model}} = useAppSelector(selectUpdated{{pascalCase model}});\n' +
            '\tconst update{{pascalCase model}}Status = useAppSelector(selectUpdate{{pascalCase model}}Status);\n' +
            '\tconst update{{pascalCase model}}Error = useAppSelector(selectUpdate{{pascalCase model}}Error);\n\n' +
            '\tconst handleDelete{{pascalCase model}} = (id: number) => {\n' +
            '\t\tdispatch(delete{{pascalCase model}}(id));\n' +
            '\t};\n\n' +
            '\tconst deleted{{pascalCase model}} = useAppSelector(selectDeleted{{pascalCase model}});\n' +
            '\tconst delete{{pascalCase model}}Status = useAppSelector(selectDelete{{pascalCase model}}Status);\n' +
            '\tconst delete{{pascalCase model}}Error = useAppSelector(selectDelete{{pascalCase model}}Error);',
          abortOnFail: true,
        },
      ];

      if (wantImportsOnly) return actions;

      actions = actions.concat([
        {
          type: 'add',
          path: 'src/models/{{pascalCase model}}.ts',
          templateFile: 'plop-templates/models/Model.ts.hbs',
          skipIfExists: true,
        },
        {
          type: 'add',
          path: 'src/models/index.ts',
          templateFile: 'plop-templates/models/index.ts.hbs',
          skipIfExists: true,
        },
        {
          type: 'append',
          path: 'src/models/index.ts',
          pattern: '/* MODEL_EXPORTS */',
          template: "export * from './{{pascalCase model}}';",
        },
        {
          type: 'add',
          path: 'src/controllers/{{camelCase model}}Controller.ts',
          templateFile: 'plop-templates/controllers/modelController.ts.hbs',
          skipIfExists: true,
        },
        {
          type: 'append',
          path: 'src/controllers/index.ts',
          pattern: '/* CONTROLLER_EXPORTS */',
          template: "export * from './{{camelCase model}}Controller';",
        },
        {
          type: 'add',
          path: 'src/requests/{{camelCase model}}Request.ts',
          templateFile: `plop-templates/requests/${
            wantFormikYup ? 'modelRequest' : 'modelRequest-base'
          }.ts.hbs`,
          skipIfExists: true,
        },
        {
          type: 'append',
          path: 'src/requests/index.ts',
          pattern: '/* REQUEST_EXPORTS */',
          template: "export * from './{{camelCase model}}Request';",
        },
        {
          type: 'add',
          path: 'src/store/{{pascalCase model}}/{{camelCase model}}Slice.ts',
          templateFile: 'plop-templates/store/Model/modelSlice.ts.hbs',
          skipIfExists: true,
        },
        {
          type: 'add',
          path: 'src/store/{{pascalCase model}}/index.ts',
          templateFile: 'plop-templates/store/Model/index.ts.hbs',
          skipIfExists: true,
        },
        {
          type: 'append',
          path: 'src/store/index.ts',
          pattern: "import { Action, configureStore, ThunkAction } from '@reduxjs/toolkit';",
          template: "import { {{pascalCase model}}Reducer } from './{{pascalCase model}}';",
        },
        {
          type: 'append',
          path: 'src/store/index.ts',
          pattern: 'reducer: {',
          template: '\t\t{{camelCase model}}: {{pascalCase model}}Reducer,'
        },
        {
          type: 'append',
          path: 'src/store/index.ts',
          pattern: '/* STORE_EXPORTS */',
          template: "export * from './{{pascalCase model}}';",
        },
      ]);

      return actions;
    }
  });

  plop.setGenerator('env', {
    description: 'Generate env',
    prompts: [],
    actions: [
      {
        type: 'add',
        path: './.env',
        templateFile: 'plop-templates/.env.hbs',
        skipIfExists: true,
      },
    ],
  });
};
