package io.chrisdavenport.github.endpoints.utils

import org.http4s.{Header, Uri}
import cats.implicits._

/**
 * Can be mixed-in to simulate pagination in tests
 */
trait Paginate {

  /**
   * Creates the headers for each pages
   * @param uri The base uri
   * @param numPages The number of pages
   * @return A map, containing the "Link" header for each page point to prev, next, first and last page
   */
  def paginate(uri: Uri, numPages: Int): Map[Int, Header] = {
    (1 to numPages).map { currentPage =>
      currentPage -> Header(
        "Link",
        List(
          prevPage(uri, currentPage),
          nextPage(uri, numPages, currentPage),
          lastPage(uri, numPages, currentPage),
          firstPage(uri, currentPage)
        )
          .flatten
          .map { case (uri, rel) =>
            s""" <${uri.toString}>; rel="$rel""""
          }. mkString(",")
      )
    }.toMap
  }

  private def firstPage(uri: Uri, page: Int): Option[(Uri, String)] =
    Option(page)
      .filterNot(_ == 1)
      .as((uri.withQueryParam[String, String]("page", "1"), "first"))

  private def prevPage(uri: Uri, page: Int): Option[(Uri, String)] =
    Option(page)
      .filterNot(_ == 1)
      .map { p =>
        (uri.withQueryParam[String, String]("page", (p - 1).toString), "prev")
      }

  private def nextPage(uri: Uri, numPages: Int, page: Int): Option[(Uri, String)] =
    Option(page)
      .filterNot(_ == numPages)
      .map { p =>
        (uri.withQueryParam[String, String]("page", (p + 1).toString), "next")
      }

  private def lastPage(uri: Uri, numPages: Int, page: Int): Option[(Uri, String)] =
    Option(page)
      .filterNot(_ == numPages)
      .as((uri.withQueryParam[String, String]("page", numPages.toString), "last"))

}