#!/bin/bash

# genTree CONSTANTS
projectName=""
prisma=""
tailwind=""
reactQuery=""

# (dirName, dirLocation)
function makeDir() {
  if [ "$2" == "" ]; then
    mkdir $1
  else
    initLocation=$(pwd)
    cd $2
    mkdir $1
    cd $initLocation
  fi
}

# (fileName, dirLocation)
function makeFile() {
  if [ "$2" == "" ]; then
    touch $1
  else
    initLocation=$(pwd)
    cd $2
    touch $1
    cd $initLocation
  fi
}

function makeServerDir() {
  makeDir server
  makeDir dist server
  makeDir src server

  echo "import express from 'express';
import path from 'path';
import dotenv from 'dotenv';

dotenv.config();

const app = express();

const CLIENT = path.join(__dirname, '../../client/dist');
const PORT = process.env.PORT || 3000;
const HOST = process.env.HOST || 'localhost';
const URL = \`http://\${HOST}:\${PORT}\`;

app.use(express.static(CLIENT));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.listen(3000, () => console.log(\`Hosted at: \${URL}\`));

export {};" >server/src/index.ts

  makeFile index.ts server/src
  makeDir routes server/src
  makeFile index.ts server/src/routes
  makeDir model server/src
  makeFile index.ts server/src/model
  makeDir controller server/src
  makeFile index.ts server/src/controller
  makeDir database server/src
  makeFile index.ts server/src/database
}

function makeClientDir() {
  makeDir client
  makeDir dist client
  makeDir src client

  echo "<!DOCTYPE html>
<html lang=\"en\">
  <head>
    <meta charset=\"UTF-8\" />
    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />
    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />
    <link rel=\"stylesheet\" href=\"output.css\">
    <title>$projectName</title>
  </head>
  <body>
    <div id=\"root\"></div>
    <script src=\"./bundle.js\"></script>
  </body>
</html>

  " >client/dist/index.html

  makeDir assets client/dist

  if [ "$reactQuery" == "true" ]; then
    echo "import React from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
import App from './components/App';
import './index.css';

const queryClient = new QueryClient();

const root = createRoot(document.getElementById('root') as Element);
root.render(
  <QueryClientProvider client={queryClient}>
    <App />
    <ReactQueryDevtools />
  </QueryClientProvider>
);
" >client/src/index.tsx
  else
    echo "import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './components/App';
import './index.css';

const root = createRoot(document.getElementById('root') as Element);
root.render(
    <App />
);
" >client/src/index.tsx
  fi

  if [ "$tailwind" == "true" ]; then
    echo "@tailwind base;
@tailwind components;
@tailwind utilities;

*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  padding: 0;
  margin: 0;
  font: inherit;
}

html {
  color-scheme: dark light;
}

body {
  min-height: 100vh;
}

.list {
  list-style-type: none;
}

img, picture, svg, video {
  display: block;
  width: 100%;
}" >client/src/index.css
  else
    echo "*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  padding: 0;
  margin: 0;
  font: inherit;
}

html {
  color-scheme: dark light;
}

body {
  min-height: 100vh;
}

.list {
  list-style-type: none;
}

img, picture, svg, video {
  display: block;
  width: 100%;
}" >client/src/index.css
  fi

  makeDir components client/src

  echo "import React from 'react';

export default function App() {
  return (
    <div>
      <h1>$projectName</h1>
    </div>
  );
}
" >client/src/components/App.tsx
}

function makeTypesDir() {
  makeDir @types
  makeFile index.d.ts @types
}

function makeRootFiles() {
  echo "# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
bundle.js
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port" >.gitignore

  echo "index.html
__tests__
ignore
webpack.config.js
tailwind.config.js" >.eslintignore

  echo "{
  \"compilerOptions\": {
    \"target\": \"es2016\",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    \"module\": \"commonjs\",                                /* Specify what module code is generated. */
    \"jsx\": \"react\",                                /* Specify what JSX code is generated. */
    \"moduleResolution\": \"node\",
    \"esModuleInterop\": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    \"forceConsistentCasingInFileNames\": true,            /* Ensure that casing is correct in imports. */
    \"strict\": true,                                      /* Enable all strict type-checking options. */
    \"skipLibCheck\": true,                                 /* Skip type checking all .d.ts files. */
  },
    \"include\": [\"./client/src\"],
}" >tsconfig.json

  echo "{
  \"compilerOptions\": {
    \"target\": \"es2016\",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    \"module\": \"commonjs\",                                /* Specify what module code is generated. */
    \"rootDir\": \"./server/src\",                                  /* Specify the root folder within your source files. */
    \"outDir\": \"./server/dist\",                                   /* Specify an output folder for all emitted files. */
    \"esModuleInterop\": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    \"forceConsistentCasingInFileNames\": true,            /* Ensure that casing is correct in imports. */
    \"strict\": true,                                      /* Enable all strict type-checking options. */
    \"skipLibCheck\": true                                 /* Skip type checking all .d.ts files. */
  },
  \"include\": [\"./server/src\"],
}" >tsconfig.server.json

  if [ "$prisma" == "true" ]; then
    echo "# 3000, 8080, etc
PORT=\"3000\"

# develpment, production
ENV_MODE=\"development\"

# localhost
HOST=\"localhost\"

# https://SOME_IP_ADDRESS.com, http:EC2.com, etc
URL=\"\"

DB_USER=\"\"

DB_PW=\"\"


# This was inserted by \$(prisma init):
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL=\"postgresql://POSTGRESQL_USER:POSTGRESQL_PASSWORD@localhost:5432/DATABASE_NAME\"" >example.env
    echo "# 3000, 8080, etc
PORT=\"3000\"

# develpment, production
ENV_MODE=\"development\"

# localhost
HOST=\"localhost\"

# https://SOME_IP_ADDRESS.com, http:EC2.com, etc
URL=\"\"

DB_USER=\"\"

DB_PW=\"\"


# This was inserted by \$(prisma init):
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL=\"postgresql://POSTGRESQL_USER:POSTGRESQL_PASSWORD@localhost:5432/DATABASE_NAME\"" >.env
  else

    echo "# 3000, 8080, etc
PORT=\"3000\"

# develpment, production
ENV_MODE=\"development\"

# localhost
HOST=\"localhost\"

# https://SOME_IP_ADDRESS.com, http:EC2.com, etc
URL=\"\"

DB_USER=\"\"

DB_PW=\"\"" >example.env

    echo "# 3000, 8080, etc
PORT=\"3000\"

# develpment, production
ENV_MODE=\"development\"

# localhost
HOST=\"localhost\"

# https://SOME_IP_ADDRESS.com, http:EC2.com, etc
URL=\"\"

DB_USER=\"\"

DB_PW=\"\"" >.env
  fi

  if [ "$prisma" == "true" ]; then
    echo "#!/usr/bin/env

# node version 18.7.0 doesnt work with prisma, so running this file will downgrade 
  # the terminal upon execution.

export NVM_DIR=\$HOME/.nvm;
source \$NVM_DIR/nvm.sh;
echo -n \"Switching from node \"
node -v
nvm use 16.17
nodemon server/dist/index.js" >runServer.sh
  fi

  echo "# SET UP

1. Add in package.json scripts:
\`\`\`json
\"scripts\": {
  \"start\": \"webpack -w\",
  \"client-build\": \"webpack\",
  \"client-dev\": \"webpack -w\",
  \"server\": \"bash runServer.sh\",
  \"server-build\": \"tsc --project tsconfig.server.json\",
  \"server-dev\": \"tsc --project tsconfig.server.json -w\",
  \"format\": \"prettier -w\",
  \"build-css\": \"npx tailwindcss -i ./client/src/index.css -o ./client/dist/output.css --watch\"
  },
\`\`\`

2. Run 4 scripts in order:
\`\`\`
npm run build-css
npm run server-dev
npm run client-dev
npm run server
\`\`\`

___________________________________

## Install Dependencies:
\`\`\`sh
npm init -y

npm i react react-dom axios dotenv express react-query

npm i -D nodemon typescript webpack webpack-cli eslint-config-airbnb ts-loader eslint-config-airbnb-typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin css-loader style-loader tailwindcss

npm i -D @types/react @types/react-dom @types/express @types/react-query @types/node @types/express 

npx install-peerdeps -D eslint-config-airbnb
npx tsc --init
npx tailwindcss init
npx prisma init
\`\`\`

# Note: Commented out = optional


### tsconfig.json edits:
\`\`\`json
{
  \"compilerOptions\": {
    \"target\": \"es2016\",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    \"module\": \"commonjs\",                                /* Specify what module code is generated. */
    \"jsx\": \"react\",                                /* Specify what JSX code is generated. */
    \"moduleResolution\": \"node\",
    \"esModuleInterop\": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    \"forceConsistentCasingInFileNames\": true,            /* Ensure that casing is correct in imports. */
    \"strict\": true,                                      /* Enable all strict type-checking options. */
    \"skipLibCheck\": true,                                 /* Skip type checking all .d.ts files. */
  },
    \"include\": [\"./client/src\"],
}
\`\`\`

### Setup .eslintrc 
\`\`\`json
{
  \"extends\":[
    \"airbnb\",
    \"airbnb-typescript\",
    /* 
    \"plugin:react-hooks/recommended\",
    \"plugin:prettier/recommended\",
    \"plugin:react/recommended\"
    */
  ],
  \"env\": {
    \"browser\": true
  },
  \"parser\": \"@typescript-eslint/parser\",
  \"parserOptions\": {
    \"project\": \"./tsconfig.json\"
  },
  \"rules\": {
    \"no-tabs\":\"off\",
    \"no-console\":\"off\",
    /* 
    \"no-void\": \"off\",
    \"react-hooks/exhaustive-deps\": [
      \"warn\",
      {
        \"additionalHooks\": \"(useMyCustomHook|useMyOtherCustomHook)\"
      }
    ]
    */
  }
  // \"plugins\": [\"prettier\", \"react\", \"react-hooks\"]
}
\`\`\`

### Webpack
\`\`\`js
const path = require('path');

module.exports = {
  entry: path.join(__dirname, 'client/src'),
  output: {
    path: path.join(__dirname, 'client/dist'),
    filename: 'bundle.js',
  },
  mode: 'development',
  module: {
    rules: [
      {
        test: /\\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
      /* {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      }, */
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
};
\`\`\`

### .prettierrc
\`\`\`json
{
  \"trailingComma\": \"es5\",
  \"printWidth\": 100,
  \"semi\": true,
  \"singleQuote\": true,
  \"tabWidth\": 2
}
\`\`\`

### tailwind.config.js
\`\`\`js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [\"./client/src/**/*.{html,js}\"],
  theme: {
    extend: {},
  },
  plugins: [],
}
\`\`\`

### package.json
\`\`\`json
\"scripts\": {
  \"start\": \"webpack -w\",
  \"client-build\": \"webpack\",
  \"client-dev\": \"webpack -w\",
  \"server\": \"bash runServer.sh\",
  \"server-build\": \"tsc --project tsconfig.server.json\",
  \"server-dev\": \"tsc --project tsconfig.server.json -w\",
  \"format\": \"prettier -w\",
  \"build-css\": \"npx tailwindcss -i ./client/src/index.css -o ./client/dist/output.css --watch\"
  }
\`\`\`" >SET_UP.md

  echo "# $projectName" >README.md

  echo "{
  \"extends\": [
    \"airbnb\",
    \"airbnb-typescript\",
    \"plugin:react-hooks/recommended\",
    \"plugin:prettier/recommended\",
    \"plugin:react/recommended\"
  ],
  \"env\": {
    \"browser\": true
  },
  \"parser\": \"@typescript-eslint/parser\",
  \"parserOptions\": {
    \"project\": [\"./tsconfig.json\", \"./tsconfig.server.json\", \"./webpack.config.js\", \"./tailwind.config.js\"]
  },
  \"rules\": {
    \"no-tabs\": \"off\",
    \"no-console\": \"off\",
    \"no-alert\": \"off\",
    \"no-void\": \"off\",
    \"react/no-array-index-key\": \"off\",
    \"react-hooks/exhaustive-deps\": [
      \"warn\",
      {
        \"additionalHooks\": \"(useMyCustomHook|useMyOtherCustomHook)\"
      }
    ]
  },
  \"plugins\": [\"prettier\", \"react\", \"react-hooks\"]
}" >.eslintrc

  echo "{
  \"trailingComma\": \"es5\",
  \"printWidth\": 100,
  \"semi\": true,
  \"singleQuote\": true,
  \"tabWidth\": 2
}" >.prettierrc

  echo "const path = require(\"path\");
const env = require(\"dotenv\").config().parsed;

const MODE = env.ENV_MODE | \"development\";

module.exports = {
  entry: path.join(__dirname, \"client/src\"),
  output: {
    path: path.join(__dirname, \"client/dist\"),
    filename: \"bundle.js\",
  },
  mode: \"development\",
  module: {
    rules: [
      {
        test: /\\.tsx?$/,
        use: \"ts-loader\",
        exclude: /node_modules/,
      },
      {
        test: /\\.css$/,
        use: [\"style-loader\", \"css-loader\"],
      },
    ],
  },
  resolve: {
    extensions: [\".tsx\", \".ts\", \".js\"],
  },
};" >webpack.config.js

  echo "/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [\"./client/src/**/*.{html,js}\"],
  theme: {
    extend: {},
  },
  plugins: [],
};
" >tailwind.config.js
}

function makeDirectoryTree() {
  projectName=$1
  prisma=$2
  tailwind=$3
  reactQuery=$4

  makeServerDir
  makeClientDir
  makeTypesDir
  makeRootFiles
}

#
#
#
#
#
#
# FILES SEPERATED
#
#
#
#
#
#

# CONSTANTS
techStack=""
installNPM="false"
installPrisma="false"
installTailwind="false"
installReactQuery="false"
# TODO: Implement {
installJest="false"
installTypescript="true"
installReact="true"
installPrettier="true"
installAirbnb="true"
# DB:
installPSQL="true"
installMongo="false"
installMySQL="flase"
#                 }

function installPackage() {
  if [ "$1" == 'Prisma' ]; then
    installPrisma="true"
  elif [ "$1" == 'Tailwind' ]; then
    installTailwind="true"
  elif [ "$1" == 'React Query' ]; then
    installReactQuery="true"
  elif [ "$1" == 'Jest' ]; then
    installJest="true"
  elif [ "$1" == 'Typescript' ]; then
    installTypescript="true"
  elif [ "$1" == 'React' ]; then
    installReact="true"
  elif [ "$1" == 'Prettier' ]; then
    installPrettier="true"
  elif [ "$1" == 'Airbnb' ]; then
    installAirbnb="true"
  elif [ "$1" == 'PSQL' ]; then
    installPSQL="true"
  elif [ "$1" == 'Mongo' ]; then
    installMongo="true"
  elif [ "$1" == 'MySQL' ]; then
    installMySQL="true"
  # elif [ "$1" == 'Airbnb' ]; then
  #   installAirbnb="true"
  fi
  techStack+="
  -$1"
}

# Prompts
echo "Project Name?"
read newProject

PS3="
Enter a number: "
echo "Install NPM:"
select choice in "npm init -y (default)" "npm init (custom)" "install later"; do
  if [ $REPLY == 1 ]; then
    installNPM="default"
    techStack+="  NPM (Default)
  -Typescript
  -React
  -Node
  -Express
  -PostgreSQL"
    echo "NPM: Default
    "
  elif [ $REPLY == 2 ]; then
    installNPM="custom"
    techStack+="  NPM (Custom)
  -Typescript
  -React
  -Node
  -Express
  -PostgreSQL"
    echo "NPM: Custom
    "
  else
    installNPM="false"
    echo "NPM: Skip
    "
  fi
  break
done

echo "Build your TechStack:
"

if [ "$installNPM" != "false" ]; then
  # TODO: Add Jest, TS, React, Prettier, Airbnb, DB choice (Postgres, mongo, mysql, etc), when I get the process down
  for element in "Prisma" "Tailwind" "React Query"; do
    read -p "Using $element? (y): " response
    if [ "$response" == "y" ] || [ "$response" == "Y" ]; then
      installPackage "$element"
    fi
  done
fi

# Build Directory
mkdir $newProject
cd $newProject

# genTree.sh "$newProject" "$installPrisma" "$installTailwind" "$installReactQuery"
makeDirectoryTree "$newProject" "$installPrisma" "$installTailwind" "$installReactQuery"

# Install Depenencies
if [ "$installNPM" == "default" ]; then
  npm init -y
fi

if [ "$installNPM" == "custom" ]; then
  npm init
fi

if [ "$installNPM" == "default" ] || [ "$installNPM" == "custom" ]; then

  npm i axios dotenv express
  npm i -D nodemon

  if [ "$installReact" == "true" ]; then
    npm i -D webpack webpack-cli css-loader style-loader
    if [ "$installReactQuery" == "true" ]; then
      npm i react react-dom react-query
    else
      npm i react react-dom
    fi
  fi

  # npm i -D nodemon typescript webpack webpack-cli eslint-config-airbnb ts-loader eslint-config-airbnb-typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin css-loader style-loader @types/react @types/react-dom @types/express @types/react-query @types/node @types/express
  # npm i -D nodemon typescript webpack webpack-cli eslint-config-airbnb ts-loader eslint-config-airbnb-typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin css-loader style-loader @types/react @types/react-dom @types/express @types/react-query @types/node @types/express

  if [ "$installTypescript" == "true" ]; then
    if [ "$installAirbnb" == "true" ]; then
      npm i -D eslint-config-airbnb-typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin
    fi
    if [ "$installReact" == "true" ]; then
      npm i -D ts-loader
    fi
    if [ "$installReactQuery" == "true" ]; then
      npm i -D @types/react @types/react-dom @types/react-query
    fi
    if [ "$installAirbnb" == "true" ]; then
      npm i -D eslint-config-airbnb-typescript @typescript-eslint/eslint-plugin
    fi
    # Inactive:
    if [ "$installJest" == "true" ]; then
      if [ "$installReact" == "true" ]; then
        npm i @testing-library/react
      fi
      npm i jest ts-jest ts-node jsdom jest-environment-jsdom@testing-library/jest-dom
      npm i -D @types/jest
      npx ts-jest config:init
    fi
    npm i -D typescript @types/express @types/node @types/express
    # npm i -D typescript ts-loader eslint-config-airbnb-typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin @types/react @types/react-dom @types/express @types/react-query @types/node @types/express
    # npx tsc --init
    npx tsc
  fi

  if [ "$installPrettier" == "true" ]; then
    npm i -D prettier eslint-config-prettier eslint-plugin-prettier
  fi

  if [ "$installAirbnb" == "true" ]; then
    npx install-peerdeps -D eslint-config-airbnb
  fi

  if [ "$installPrisma" == "true" ]; then
    npx prisma init
  fi
  if [ "$installPrisma" == "false" ] && [ "$installPSQL" == "true" ]; then
    npm i -D pg
  fi
  if [ "$installPrisma" == "false" ] && [ "$installMySQL" == "true" ]; then
    npm i -D mysql2
  fi
  if [ "$installPrisma" == "false" ] && [ "$installMongo" == "true" ]; then
    npm i -D mongoose mongodb
  fi

  if [ "$installTailwind" == "true" ]; then
    # npx tailwindcss init
    npm install -D tailwindcss
  fi

  # Sorted
  # npm i react react-dom axios dotenv express
  # npm i -D nodemon typescript webpack webpack-cli eslint-config-airbnb ts-loader eslint-config-airbnb-typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin css-loader style-loader prettier eslint-config-prettier eslint-plugin-prettier
  # npm i -D @types/react @types/react-dom @types/express @types/react-query @types/node @types/express
  # npx install-peerdeps -D eslint-config-airbnb
  # npx tsc --init
fi
echo "

_________________________________

    Dependencies Installed. 
_________________________________

"
if [ "$installNPM" == "false" ]; then
  echo "Directory Tree Built!"
else
  echo "
Tech-Stack: 

$techStack
"
fi
echo "..
_________________________________

    $newProject Created!
_________________________________"
