 ")
 
 (defvar leetcode--check-mark "✓")
-(defvar leetcode--problem-buffer-name "*leetcode:problem*")
+(defconst leetcode--buffer-name "*leetcode*")
+(defconst leetcode--description-buffer-name "*leetcode-description*")
+(defconst leetcode--submit-buffer-name "*leetcode-submit*")
 
 ;;; Login
 ;; URL
 (defconst leetcode--domain "leetcode.com")
 (defconst leetcode--base-url "https://leetcode.com")
 (defconst leetcode--url-login (concat leetcode--base-url "/accounts/login"))
+(defconst leetcode--problems-submission (concat leetcode--base-url "/problems/%s/submissions/"))
+(defconst leetcode--submission (concat leetcode--base-url "/submissions/detail/%s/check/"))
 
 ;; Cookie
 (defconst leetcode--csrftoken "csrftoken")
 (defconst leetcode--cookie-localpart-separator "/")
 
 ;; Header
-(defconst leetcode--Referer          `("Referer" . ,leetcode--url-login))
 (defconst leetcode--User-Agent       '("User-Agent" . "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:66.0) Gecko/20100101 Firefox/66.0"))
 (defconst leetcode--X-Requested-With '("X-Requested-With" . "XMLHttpRequest"))
 (defconst leetcode--X-CSRFToken      "X-CSRFToken")

(defconst leetcode--api-root (concat leetcode--base-url "/api"))
(defconst leetcode--api-all-problems (concat leetcode--api-root "/problems/all/"))
(defconst leetcode--api-graphql (concat leetcode--base-url "/graphql"))

(defun leetcode--gen-csrf-token ()
  "Knock knock, please generate the csrf token."
  (url-retrieve-synchronously leetcode--url-login))

(defun leetcode--csrf-token ()
  (let (token)
    (catch 'break
      (dolist (cur (url-cookie-retrieve leetcode--domain leetcode--cookie-localpart-separator t))
        (when (string-equal leetcode--csrftoken (aref cur 1))
          (setq token (aref cur 2))
          (throw 'break "Found the csrf token"))))
    (unless token
      (leetcode--gen-csrf-token)
      (setq token (leetcode--csrf-token)))
    token))

(defun leetcode-login (account password)
  (interactive (unless (and (boundp 'account) (boundp 'password))
                 (list (read-string "account: ")
                       (read-passwd "password: "))))
  (let* ((csrftoken (leetcode--csrf-token))
         (url-request-method "POST")
         (url-request-extra-headers (list leetcode--Referer
                                          leetcode--User-Agent
                                          leetcode--X-Requested-With
                                          (cons leetcode--X-CSRFToken csrftoken)))
         (furl-request-data (list (cons leetcode--login-csrf-key csrftoken)
                                  (cons leetcode--login-account account)

(defun leetcode ()
  "Show leetcode problems buffer."
  (interactive)
  (unless (get-buffer leetcode--buffer-name)
    (leetcode--login leetcode--login-account leetcode--login-password)
    (with-current-buffer (get-buffer-create leetcode--buffer-name)
      (leetcode-problems-mode)
      (leetcode-problems-refresh)))
  (switch-to-buffer (get-buffer leetcode--buffer-name))
  (tabulated-list-print t))

;; TODO
;; (defun leetcode-test ()
;;   "Test the code using customized testcase."
;;   )

(defun leetcode-submit ()
  "Submit the code. "
  (interactive)
  (let* ((code (buffer-substring-no-properties (point-min) (point-max)))
         (title-slug (with-current-buffer (current-buffer)
                       (file-name-base (buffer-name))))
         (id (catch 'break
               (dolist (p (plist-get leetcode--problems :problems))
                 (when (string-equal title-slug (leetcode--title-slug (plist-get p :title)))
                   (throw 'break (plist-get p :id)))))))
    (let* ((csrftoken (leetcode--csrf-token))
           (url-request-method "POST")
           (url-request-extra-headers (list leetcode--User-Agent
                                            (leetcode--referer (format leetcode--problems-submission title-slug))
                                            (cons "Content-Type" "application/json")
                                            (cons leetcode--X-CSRFToken csrftoken)))
           (url-request-data (json-encode `((lang . ,leetcode-prefer-language)
                                            (question_id . ,id)
                                            (typed_code . ,code)))))
      (with-current-buffer (url-retrieve-synchronously (format leetcode--api-submit title-slug))
        (let ((submission-id (alist-get 'submission_id (json-read)))
              (url-request-method "POST")
              (url-request-extra-headers (list leetcode--User-Agent
                                               (leetcode--referer (format leetcode--problems-submission title-slug))
                                               (cons leetcode--X-CSRFToken csrftoken))))
          (let ((res (with-current-buffer (url-retrieve-synchronously (format leetcode--submission submission-id))
                       (json-read))))
            ;; poll until the state is SUCCESS
            (while (not (string-equal (alist-get 'state res) "SUCCESS"))
              (sleep-for 0.5)
              (setq res (with-current-buffer (url-retrieve-synchronously (format leetcode--submission submission-id))
                          (json-read))))
            (let ((runtime (alist-get 'status_runtime res))
                  (memory (alist-get 'status_memory res))
                  (runtime-perc (alist-get 'runtime_percentile res))
                  (memory-perc (alist-get 'memory_percentile res))
                  (total-correct (alist-get 'total_correct res))
                  (total-testcases (alist-get 'total_testcase res))
                  (status-msg (alist-get 'status_msg res))
                  (lang (alist-get 'pretty_lang res))
                  (display-buffer-alist (cons `(,leetcode--submit-buffer-name
                                                (display-buffer-reuse-window
                                                 display-buffer-in-side-window)
                                                (reusable-frames . visible)
                                                (side . bottom)
                                                (window-height . 0.3))
                                              display-buffer-alist)))
              (with-current-buffer (get-buffer-create leetcode--submit-buffer-name)
                (erase-buffer)
                (insert (format "Status: %s\n\n" status-msg))
                (when (string-equal status-msg "Accepted")
                  (insert (format "Runtime: %s, faster than %.2f%% of %s submissions.\n\n" runtime runtime-perc lang))
                  (insert (format "Memory Usage: %s, less than %.2f%% of %s submissions." memory memory-perc lang)))
                (display-buffer (current-buffer))))))))))

(defvar leetcode-problems-mode-map
  (let ((map (make-sparse-keymap)))
