package ru.yandex.featurer.service

import java.time.{Instant, ZonedDateTime}
import java.time.chrono.IsoChronology
import java.time.format.{ResolverStyle, DateTimeFormatter, SignStyle, DateTimeFormatterBuilder}
import java.time.temporal.ChronoField

import akka.util.Timeout
import com.typesafe.config.Config
import org.slf4j.LoggerFactory
import ru.yandex.featurer.data.{ApplicationInfo, FeatureId, TeamcityArtifactInfo}
import ru.yandex.featurer.utils.application.ActorSystemComponent
import spray.client.pipelining._
import spray.http._

import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
import scala.language.postfixOps
import scala.xml.XML

/**
  * @author avhaliullin
  */
trait TeamcityServiceComponent {
  component: ActorSystemComponent =>

  def teamcityService: TeamcityService

  class TeamcityService(config: TeamcityService.TeamcityServiceConfig) {
    private val log = LoggerFactory.getLogger(getClass)

    private val teamcityDateTimeFromat = DateTimeFormatter.ofPattern( """yyyyMMdd'T'HHmmssZ""")

    private implicit lazy val dispatcher = actorSystem.dispatcher

    private lazy val pipeline: HttpRequest => Future[HttpResponse] = sendReceive

    private implicit val timeout: Timeout = 10 seconds

    private implicit val instantOrdering = Ordering.fromLessThan[ZonedDateTime](_ isBefore _)

    private val authHeader = HttpHeaders.Authorization(GenericHttpCredentials(config.authScheme, config.authToken))

    def getArtifactInfo(applicationInfo: ApplicationInfo, featureId: FeatureId): Option[TeamcityArtifactInfo] = {
      val address = config.address
      val uri = Uri(s"$address/httpAuth/app/rest/builds/buildType:id:${applicationInfo.teamcityBuildId.id},branch:name:${featureId.id}/artifacts/")
      val req = Get(uri).withHeaders(authHeader)
      val resp = Await.result(pipeline(req), 10 seconds)
      resp.status match {
        case StatusCodes.NotFound => None
        case StatusCodes.OK =>
          resp.entity match {
            case HttpEntity.NonEmpty(ContentType(MediaTypes.`application/xml`, charsetOpt), data) =>
              val xmlString = data.asString(charsetOpt.getOrElse(HttpCharsets.`UTF-8`))
              log.info("Teamcity response:\n" + xmlString)
              val xml = XML.loadString(xmlString)
              val artifacts = (for {
                file <- xml \\ "file"
                href <- file \ "content" \ "@href"
                modDate <- file \ "@modificationTime"
                name <- file \ "@name"
              } yield (href.toString(), modDate.toString(), name.toString())).map {
                case (href, modTimeString, name) =>
                  val modTime = ZonedDateTime.parse(modTimeString, teamcityDateTimeFromat)
                  TeamcityArtifactInfo(applicationInfo.appId, featureId, Uri(address + href), modTime, name)
              }
              if (artifacts.isEmpty) {
                None
              } else {
                Some(artifacts.maxBy(_.modifiedDate))
              }

            case _ => throw new RuntimeException("Unexpected content type in teamcity response\n" + resp)
          }
        case _ => throw new RuntimeException("Bad teamcity status code in response:\n" + resp)
      }
    }
  }

  object TeamcityService {

    case class TeamcityServiceConfig(address: String, authScheme: String, authToken: String)

    object TeamcityServiceConfig {
      implicit def apply(config: Config): TeamcityServiceConfig =
        TeamcityServiceConfig(
          address = config.getString("baseUrl"),
          authScheme = config.getString("authScheme"),
          authToken = config.getString("authString")
        )
    }

  }

}
