(ns leiningen.new.python
  (:require [leiningen.new.templates :refer [renderer name-to-path ->files
                                             *dir* sanitize]]
            [clojure.string :as string]
            [clojure.java.io :as io]
            [clojure.java.shell :as shell]
            [leiningen.core.main :as main])
  (:import (java.util Calendar)
           (java.text SimpleDateFormat)))

(def render (renderer "python"))

(defn get-template-file
  "return a URL for given template path"
  [template]
  ;; io/resource returns the URL for a named resource. it finds it in
  ;; java classpath.
  (io/resource (str "leiningen/new/python/" template)))

(defn sh-output
  "return shell/sh's output, either stdout or stderr"
  [result]
  (let [output (str (:out result)
                    (:err result))]
    (if (not (empty? output))
      (main/info output))))

(defn name-to-python-pkg-name
  "convert project name to python pkg name"
  [name]
  (string/replace name "-" "_"))

(defn python
  "a standard python project with virtualenv, githooks, pep8, pylint etc"
  [name]
  (let [data {:name name
              :sanitized (name-to-path name)
              :python-pkg-name (name-to-python-pkg-name name)
              :date (let [cal (Calendar/getInstance)]
                      (.format (new SimpleDateFormat "yyyy-MM-dd")
                               (.getTime cal)))}
        dot-template-files ["gitignore"
                            ]
        template-files ["Makefile"
                        "fabfile.py"
                        "tox.ini"
                        "requirements.txt"
                        "requirements-dev.txt"
                        "setup.py"
                        "Dockerfile.template"
                        "app.yaml"
                        "README.rst"
                        "README-dev.rst"
                        "utils/versionutils.py"
                        "utils/build-deb"
                        "utils/install-git-hooks"
                        "utils/trigger-jenkins-build"
                        "deb-scripts/before-install.sh"
                        "deb-scripts/after-install.sh"
                        "deb-scripts/before-remove.sh"
                        "conf/http.site"
                        "conf/https.site"
                        "conf/logrotate.conf"
                        "conf/uwsgi.ini"
                        "conf/uwsgi.upstart"
                        "conf/web.upstart"
                        "conf/web.service"
                        "git-hooks/post-commit"
                        "git-hooks/post-merge"
                        "git-hooks/pre-commit"
                        ]
        plain-files ["utils/__init__.py"
                     "utils/choose_default_python.sh"
                     ;; virtualenv
                     "utils/virtualenv-16.4.1/virtualenv_support/pip-19.0.3-py2.py3-none-any.whl"
                     "utils/virtualenv-16.4.1/virtualenv_support/setuptools-40.8.0-py2.py3-none-any.whl"
                     "utils/virtualenv-16.4.1/virtualenv_support/wheel-0.33.1-py2.py3-none-any.whl"
                     "utils/virtualenv-16.4.1/virtualenv_support/__init__.py"
                     "utils/virtualenv-16.4.1/virtualenv.py"
                     ]
        empty-files []
        package-files ["__init__.py"
                       "logger.conf"
                       "configlogger.py"
                       "config.py"
                       "sanity_check.py"
                       "test_main.py"]
        post-run-commands ["chmod -R +x utils/ git-hooks/ deb-scripts/"
                           ]]
    (main/info "Generating new python project:" name)
    (apply ->files data
           (concat
            (map (fn [f] [f ""]) empty-files)
            (map (fn [f] [(str "." f) (render f data)]) dot-template-files)
            (map (fn [f] [f (render f data)]) template-files)
            (map (fn [f] [(str "{{python-pkg-name}}/" f) (render f data)]) package-files)
            (map (fn [f] [f (io/input-stream (get-template-file f))])
                 ;; using input-stream here because the result of rendering
                 ;; binary file is undefined even when data is empty hashmap.
                 plain-files)))
    (let [project-dir (or *dir* name)]
      (shell/with-sh-dir project-dir
        (doseq [cmd (map (fn [cmd] (string/split cmd #" ")) post-run-commands)]
          (sh-output (apply shell/sh cmd))))
      ;; run git init if no .git found.
      (if (not (.exists (io/file project-dir ".git")))
        (sh-output (shell/sh "git" "init" :dir project-dir))))))
