;;; Guile-Git --- GNU Guile bindings of libgit2
;;; Copyright © 2025 Nicolas Graves <ngraves@ngraves.fr>
;;;
;;; This file is part of Guile-Git.
;;;
;;; Guile-Git is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or
;;; (at your option) any later version.
;;;
;;; Guile-Git is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with Guile-Git.  If not, see <http://www.gnu.org/licenses/>.

(define-module (tests stash)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-64)
  #:use-module (ice-9 ftw)
  #:use-module (ice-9 match))

(use-modules (git))
(use-modules (tests helpers))

(test-begin "stash")

(libgit2-init!)

(with-repository "simple" directory
  (let ((test-file (string-append
                    directory file-name-separator-string "stash-test.txt")))

    ;; TODO Also test with
    ;;  - STASH-DEFAULT
    ;;  - STASH-KEEP-INDEX
    ;; but we need to implement lower-level index support first in order
    ;; for those to be testable.
    ;; TODO Implement support for stash save options.

    ;; Basic stash-save with STASH-INCLUDE-UNTRACKED
    (call-with-output-file test-file
      (lambda (port)
        (format port "This is a test file for stashing")))

    (test-assert "stash-save with STASH-INCLUDE-UNTRACKED"
      (let* ((repository (repository-open directory))
             (signature (signature-now "Stash Tester" "test@localhost"))
             (message "Test stash message")
             (stash-id (stash-save repository signature message
                                   STASH-INCLUDE-UNTRACKED)))
        (and (oid? stash-id)
             ;; After stashing, the file should not exist in the working directory
             (not (file-exists? test-file)))))

    (test-assert "stash-foreach"
      (let* ((repository (repository-open directory))
             (stashes '())
             (count 0))
        (stash-foreach repository
                       (lambda (index message id)
                         (set! stashes (cons (list index message id) stashes))
                         (set! count (+ count 1))
                         0))
        (and (>= count 1)
             (equal? (length stashes) count)
             ;; Test that messages are correctly stored
             (find (lambda (s) (string-contains (cadr s) "Test stash message")) stashes))))

    (test-assert "stash-apply with default options"
      (let* ((repository (repository-open directory))
             (options (make-stash-apply-options)))
        ;; Apply the most recent stash (index 0)
        (stash-apply repository 0 options)
        ;; After applying, the files should exist again
        (and (file-exists? test-file))))


    ;; Apply again with REINSTATE-INDEX flag
    (call-with-output-file test-file
      (lambda (port)
        (format port "This is a test file for stashing")))

    (test-assert "stash-apply with REINSTATE-INDEX flag"
      (let* ((repository (repository-open directory))
             (options (make-stash-apply-options
                       #:flags STASH-APPLY-REINSTATE-INDEX))
             (signature (signature-now "Stash Tester" "test@localhost"))
             (message "Test stash message")
             (stash-id (stash-save repository signature message
                                   STASH-INCLUDE-UNTRACKED)))

        (and (oid? stash-id)
             (not (file-exists? test-file))
             (stash-apply repository 0 options)
             (file-exists? test-file))))

    (test-assert "stash-drop"
      (let* ((repository (repository-open directory))
             (before-count 0)
             (after-count 0))
        ;; Count stashes before dropping
        (stash-foreach repository
                       (lambda (index message id)
                         (set! before-count (+ before-count 1))
                         0))

        ;; Drop the most recent stash (index 0)
        (stash-drop repository 0)

        ;; Count stashes after dropping
        (stash-foreach repository
                       (lambda (index message id)
                         (set! after-count (+ after-count 1))
                         0))

        (= after-count (- before-count 1))))

    (test-assert "stash-pop with options"
      (let* ((repository (repository-open directory))
             (options (make-stash-apply-options
                       #:flags STASH-APPLY-REINSTATE-INDEX))
             (before-count 0)
             (after-count 0))
        ;; Create a new file to be stashed
        (call-with-output-file test-file
          (lambda (port)
            (display "This is a file for testing stash-pop" port)))

        (stash-save repository
                    (signature-now "Stash Tester" "test@localhost")
                    "Stash for pop test")

        ;; Count stashes before popping
        (stash-foreach repository
                       (lambda (index message id)
                         (set! before-count (+ before-count 1))
                         0))

        (stash-pop repository 0 options)

        ;; Count stashes after popping
        (stash-foreach repository
                       (lambda (index message id)
                         (set! after-count (+ after-count 1))
                         0))

        (and (= after-count (- before-count 1))
             ;; After popping, the file should exist
             (file-exists? test-file))))))

(libgit2-shutdown!)

(test-end "stash")
