(define-module (guix-qa-frontpage view issue)
  #:use-module (srfi srfi-1)
  #:use-module (ice-9 match)
  #:use-module (web uri)
  #:use-module (guix-qa-frontpage manage-builds)
  #:use-module (guix-qa-frontpage derivation-changes)
  #:use-module (guix-qa-frontpage view util)
  #:export (issue-view))

(define (issue-view issue-number series mumi-tags
                    derivation-changes
                    change-details comparison-details)

  (define comparison-link
    (and=>
     (assoc-ref change-details "revisions")
     (lambda (revisions)
       (simple-format #f "https://data.qa.guix.gnu.org/compare?base_commit=~A&target_commit=~A"
                      (assoc-ref (assoc-ref revisions "base")
                                 "commit")
                      (assoc-ref (assoc-ref revisions "target")
                                 "commit")))))

  (define* (package-derivations-comparison-link system
                                                #:key build-change)
    (let ((revisions
           (assoc-ref change-details "revisions")))
      (string-append
       (simple-format #f "https://data.qa.guix.gnu.org/compare/package-derivations?base_commit=~A&target_commit=~A&system=~A&target=none"
                      (assoc-ref (assoc-ref revisions "base")
                                 "commit")
                      (assoc-ref (assoc-ref revisions "target")
                                 "commit")
                      system)
       (if build-change
           (simple-format #f "&build_change=~A" build-change)
           ""))))

  (define tagged-as-moreinfo?
    (member "moreinfo" mumi-tags))

  (layout
   #:title (simple-format #f "Issue ~A" issue-number)
   #:head
   '((style "
td.good {
  padding: 0.05rem 0.65rem;
  font-weight: bold;

  border: 0.3rem dashed green;
}

td.bad {
  padding: 0.05rem 0.65rem;
  font-weight: bold;

  border: 0.3rem dashed red;
}

.tag {
    display: inline-block;
    padding: 0.25em 0.4em;
    margin-left: 0.25em;
    font-size: 75%;
    font-weight: 700;
    line-height: 1;
    text-align: center;
    white-space: nowrap;
    vertical-align: baseline;
    border-radius: 0.25rem;
    background-color: var(--color-accent);
}
"))
   #:body
   `((main
      (div
       (@ (class "large-screen-float-right"))
       (h3 "Links")
       (ul
        (li
         (a (@ (href ,(simple-format #f "https://issues.guix.gnu.org/~A"
                                     issue-number)))
            "View issue on issues.guix.gnu.org"))
        (li
         (a (@ (href ,(assoc-ref series "web_url")))
            "View series on Patchwork"))
        ,@(if comparison-link
              `((li
                 (a (@ (href ,comparison-link))
                    "View Guix Data Service comparison")))
              '())
        (li
         (a (@ (href
                ,(string-append
                  "mailto:control@debbugs.gnu.org?"
                  "subject="
                  (uri-encode
                   (simple-format #f "tag ~A moreinfo" issue-number))
                  "&body="
                  (uri-encode
                   (string-append
                    (simple-format #f "tags ~A ~A moreinfo"
                                   issue-number
                                   (if tagged-as-moreinfo?
                                       "-"
                                       "+"))
                    "\nquit\n")))))
            ,(if tagged-as-moreinfo?
                 "Remove moreinfo tag"
                 "Mark as moreinfo")))))

      (h2 ,(assoc-ref series "name")
          ,@(map
             (lambda (tag)
               `(span (@ (class "tag"))
                      ,tag))
             (or mumi-tags '())))

      (ul
       ,@(map
          (lambda (patch)
            `(li ,(assoc-ref patch "name")))
          (assoc-ref series "patches")))

      (div
       (h3 "Lint warnings")
       (table
        (thead
         (tr
          (th "Change")
          (th "Linter")
          (th "Message")))
        (tbody
         ,@(if comparison-details
               (if (eq? (vector-length (assoc-ref comparison-details "lint_warnings"))
                        0)
                   `((tr
                      (td (@ (colspan 3))
                          "No lint warning changes"
                          (br)
                          (small "(for lint checkers that don't require the network)"))))
                   (append-map
                    (lambda (package-warnings)
                      (map
                       (lambda (warning)
                         `(tr
                           (td (@ (style ,(string-join
                                           `("border-left-width: 0.35em;"
                                             "border-left-style: solid;"
                                             ,(string-append
                                               "border-left-color: "
                                               (if (string=? (assoc-ref warning "change")
                                                             "new")
                                                   "red"
                                                   "green"))))))
                               ,(assoc-ref warning "change"))
                           (td ,@(let ((checker (assoc-ref warning "checker")))
                                   `((span (@ (class "monospace")
                                              (style "display: block;"))
                                           ,(assoc-ref checker "name"))
                                     (p (@ (style "font-size: small;"))
                                        ,(assoc-ref checker "description")))))
                           (td ,(assoc-ref warning "message"))))
                       (vector->list
                        (assoc-ref package-warnings "warnings"))))
                    (vector->list
                     (assoc-ref comparison-details "lint_warnings"))))
               '((tr
                  (td (@ (colspan 3))
                      "Comparison unavailable")))))))

      (div
       (h3 "Package changes")
       (table
        (@ (style "border-collapse: collapse;"))
        (thead
         (tr
          (th (@ (rowspan 3)) "System")
          (th (@ (colspan 8)) "Package build status")
          (th))
         (tr
          (th (@ (colspan 4)) "Base")
          (th (@ (colspan 4)) "With patches applied")
          (th))
         (tr
          ,@(let ((header-style
                   "font-size: 80%; min-width: 3.5rem;"))
              `((th (@ (style ,header-style))
                    "Succeeding")
                (th (@ (style ,header-style))
                    "Failing")
                (th (@ (style ,header-style))
                    "Blocked")
                (th (@ (style ,header-style))
                    "Unknown")
                (th (@ (style ,header-style))
                    "Succeeding")
                (th (@ (style ,header-style))
                    "Failing")
                (th (@ (style ,header-style))
                    "Blocked")
                (th (@ (style ,header-style))
                    "Unknown")
                (th)))))
        (tbody
         ,@(if (and comparison-details
                    derivation-changes)
               (let* ((base-builds
                       (builds-by-system-excluding-cross-builds
                        derivation-changes "base"))
                      (target-builds
                       (builds-by-system-excluding-cross-builds
                        derivation-changes "target"))

                      (all-systems
                       (delete-duplicates
                        (append (map car base-builds)
                                (map car target-builds))))

                      (categorised-base-builds-by-system
                       (categorise-builds all-systems base-builds))
                      (categorised-target-builds-by-system
                       (categorise-builds all-systems target-builds)))

                 (if (null? target-builds)
                     `((tr
                        (td (@ (colspan 7))
                            "No package derivation changes"
                            (br)
                            (small "(for the following systems: "
                                   ,@(drop-right
                                      (append-map
                                       (lambda (system)
                                         `((span (@ (style "font-family: monospace;"))
                                                 ,system)
                                           ", "))
                                       %systems-to-submit-builds-for)
                                      1)
                                   ")"))))
                     (map
                      (match-lambda
                        ((system . categorised-target-builds)
                         (let ((categorised-base-builds
                                (assoc-ref categorised-base-builds-by-system
                                           system))
                               (highlighed-common
                                " "))
                           (define (count side status)
                             (length
                              (assoc-ref
                               (if (eq? side 'base)
                                   categorised-base-builds
                                   categorised-target-builds)
                               status)))

                           `(tr
                             (td (@ (class "monospace")) ,system)
                             ,@(map (lambda (status)
                                      `(td ,(count 'base status)))
                                    '(succeeding failing blocked unknown))
                             (td ,@(if (and (>= (count 'target 'succeeding)
                                                (count 'base 'succeeding))
                                            (> (count 'target 'succeeding)
                                               0))
                                       `((@ (class "good")))
                                       '())
                                 ,(count 'target 'succeeding))
                             ,(if (> (count 'target 'failing)
                                     (count 'base 'failing))
                                  `(td (@ (class "bad"))
                                       (a (@ (href ,(package-derivations-comparison-link
                                                     system
                                                     #:build-change "broken")))
                                          ,(count 'target 'failing)))
                                  `(td ,(count 'target 'failing)))
                             ,(if (> (count 'target 'blocked)
                                     (count 'base 'blocked))
                                  `(td (@ (class "bad"))
                                       ,(count 'target 'blocked))
                                  `(td ,(count 'target 'blocked)))
                             ,(if (> (count 'target 'unknown)
                                     (count 'base 'unknown))
                                  `(td (@ (class "bad"))
                                       (a (@ (href ,(package-derivations-comparison-link
                                                     system
                                                     #:build-change "unknown")))
                                          ,(count 'target 'unknown)))
                                  `(td ,(count 'target 'unknown)))
                             (td (a (@ (href
                                        ,(package-derivations-comparison-link system)))
                                    "View comparison"))))))
                      (sort
                       categorised-target-builds-by-system
                       (lambda (a b)
                         (< (or (list-index
                                 (lambda (s)
                                   (string=? (car a) s))
                                 %systems-to-submit-builds-for)
                                10)
                            (or (list-index
                                 (lambda (s)
                                   (string=? (car b) s))
                                 %systems-to-submit-builds-for)
                                10)))))))
               '((tr
                  (td (@ (colspan 10))
                      "Comparison unavailable")))))))

      (div
       (h3 "Review checklist")
       (p "This is just to help anyone reviewing the changes.")

       ,@(map
          (match-lambda
            ((id label)
             `(div
               (input (@ (type "checkbox")
                         (id ,id)))
               (label (@ (for ,id)) ,label))))
          '(("lint-warnings" "Lint warnings")
            ("license" "License")
            ("package-builds" "Package builds")
            ("package-tests" "Package tests")
            ("synopsis-and-description" "Synopsis and description")
            ("commit-messages" "Commit messages"))))

      (div
       (h3 "Badges (work in progress)")
       (img
        (@ (src ,(simple-format #f "/issue/~A/status-badge-medium.svg"
                                issue-number)))))))))