<commit_before>/*
 * Copyright 2017 Human Brain Project MIP by <NAME>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package eu.hbp.mip.woken.dao

import java.sql.{ Connection, DriverManager, ResultSet, ResultSetMetaData }
import java.time.{ OffsetDateTime, ZoneOffset }

import cats.Monad
import doobie.util.transactor.Transactor
import eu.hbp.mip.woken.config.DbConnectionConfiguration
import spray.json._
import eu.hbp.mip.woken.core.model._
import eu.hbp.mip.woken.json.yaml
import eu.hbp.mip.woken.json.yaml.Yaml

/**
  * Data Access Layer
  */
trait DAL {}

object Shapes {
  val error               = "error"
  val pfa_json            = "pfa_json"
  val pfa_experiment_json = "pfa_experiment_json"
  val pfa_yaml            = "pfa_yaml"
  val html                = "html"
  val svg                 = "svg"
  val highcharts          = "highcharts"
  val highcharts_mime     = "application/highcharts+json"
}

object JobResultsDAO {
  import Shapes._
  import doobie._
  import doobie.implicits._

  implicit val DateTimeMeta: Meta[OffsetDateTime] =
    Meta[java.sql.Timestamp].xmap(ts => OffsetDateTime.of(ts.toLocalDateTime, ZoneOffset.UTC),
                                  dt => java.sql.Timestamp.valueOf(dt.toLocalDateTime))

  type JobResultColumns =
    (String, String, OffsetDateTime, String, String, Option[String], Option[String])

  private val unsafeFromColumns: JobResultColumns => JobResult = {
    case (jobId, node, timestamp, _, function, _, Some(errorMessage)) =>
      ErrorJobResult(jobId, node, timestamp, function, errorMessage)
    case (jobId, node, timestamp, shape, function, Some(data), None) if shape == pfa_json =>
      PfaJobResult(jobId, node, timestamp, function, data.parseJson.asJsObject)
    case (jobId, node, timestamp, shape, _, Some(data), None) if shape == pfa_experiment_json =>
      PfaExperimentJobResult(jobId, node, timestamp, data.parseJson.asInstanceOf[JsArray])
    case (jobId, node, timestamp, shape, function, Some(data), None) if shape == pfa_yaml =>
      PfaJobResult(jobId, node, timestamp, function, yaml.yaml2Json(Yaml(data)).asJsObject)
    case (jobId, node, timestamp, shape, function, Some(data), None) if shape == highcharts =>
      JsonDataJobResult(jobId, node, timestamp, shape, function, data.parseJson.asJsObject)
    case (jobId, node, timestamp, shape, function, Some(data), None)
        if shape == svg || shape == html =>
      OtherDataJobResult(jobId, node, timestamp, shape, function, data)
    case (_, _, _, shape, _, _, _) =>
      throw new IllegalArgumentException(s"Cannot handle job results of shape $shape")
  }

  private val jobResultToColumns: JobResult => JobResultColumns = {
    case j: PfaJobResult =>
      (j.jobId, j.node, j.timestamp, pfa_json, j.function, Some(j.model.compactPrint), None)
    case j: PfaExperimentJobResult =>
      (j.jobId,
       j.node,
       j.timestamp,
       pfa_experiment_json,
       j.function,
       Some(j.models.compactPrint),
       None)
    case j: ErrorJobResult =>
      (j.jobId, j.node, j.timestamp, pfa_json, j.function, None, Some(j.error))
    case j: JsonDataJobResult =>
      (j.jobId, j.node, j.timestamp, j.shape, j.function, Some(j.data.compactPrint), None)
    case j: OtherDataJobResult =>
      (j.jobId, j.node, j.timestamp, j.shape, j.function, Some(j.data), None)
  }

  //implicit val JobResultMeta: Meta[JobResult] =
  // Meta[JobResultColumns].xmap(unsafeFromColumns, jobResultToColumns)

  def queryJobResults(jobId: String): ConnectionIO[List[JobResult]] =
    sql"select job_id, node, timestamp, shape, function, data, error from job_result where job_id = $jobId"
      .query[JobResultColumns]
      .list
      .map(_.map(unsafeFromColumns))

}

trait JobResultsDAL extends DAL {

  def findJobResults(jobId: String): List[JobResult]

}

class NodeDAL[M: Monad](xa: Transactor[M]) extends JobResultsDAL {
  import doobie.implicits._

  override def findJobResults(jobId: String) =
    JobResultsDAO.queryJobResults(jobId).transact(xa).unsafePerformIO

}

/*
class FederationDAL(xa: Transactor[IO]) extends JobResultsDAL {<commit_msg>WIP: cleanup<commit_after>/*
 * Copyright 2017 Human Brain Project MIP by LREN CHUV
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package eu.hbp.mip.woken.dao

import java.sql.{ Connection, DriverManager, ResultSet, ResultSetMetaData }
import java.time.{ OffsetDateTime, ZoneOffset }

import cats._
import cats.data._
import doobie._
import eu.hbp.mip.woken.config.DbConnectionConfiguration
import spray.json._
import eu.hbp.mip.woken.core.model._
import eu.hbp.mip.woken.json.yaml
import eu.hbp.mip.woken.json.yaml.Yaml

import scala.language.higherKinds

/**
  * Data Access Layer
  */
trait DAL {}

object Shapes {
  val error               = "error"
  val pfa_json            = "pfa_json"
  val pfa_experiment_json = "pfa_experiment_json"
  val pfa_yaml            = "pfa_yaml"
  val html                = "html"
  val svg                 = "svg"
  val highcharts          = "highcharts"
  val highcharts_mime     = "application/highcharts+json"
}

object JobResultsDAO {
  import Shapes._
  import doobie._
  import doobie.implicits._

  implicit val DateTimeMeta: Meta[OffsetDateTime] =
    Meta[java.sql.Timestamp].xmap(ts => OffsetDateTime.of(ts.toLocalDateTime, ZoneOffset.UTC),
                                  dt => java.sql.Timestamp.valueOf(dt.toLocalDateTime))

  type JobResultColumns =
    (String, String, OffsetDateTime, String, String, Option[String], Option[String])

  private val unsafeFromColumns: JobResultColumns => JobResult = {
    case (jobId, node, timestamp, _, function, _, Some(errorMessage)) =>
      ErrorJobResult(jobId, node, timestamp, function, errorMessage)
    case (jobId, node, timestamp, shape, function, Some(data), None) if shape == pfa_json =>
      PfaJobResult(jobId, node, timestamp, function, data.parseJson.asJsObject)
    case (jobId, node, timestamp, shape, _, Some(data), None) if shape == pfa_experiment_json =>
      PfaExperimentJobResult(jobId, node, timestamp, data.parseJson.asInstanceOf[JsArray])
    case (jobId, node, timestamp, shape, function, Some(data), None) if shape == pfa_yaml =>
      PfaJobResult(jobId, node, timestamp, function, yaml.yaml2Json(Yaml(data)).asJsObject)
    case (jobId, node, timestamp, shape, function, Some(data), None) if shape == highcharts =>
      JsonDataJobResult(jobId, node, timestamp, shape, function, data.parseJson.asJsObject)
    case (jobId, node, timestamp, shape, function, Some(data), None)
        if shape == svg || shape == html =>
      OtherDataJobResult(jobId, node, timestamp, shape, function, data)
    case (_, _, _, shape, _, _, _) =>
      throw new IllegalArgumentException(s"Cannot handle job results of shape $shape")
  }

  private val jobResultToColumns: JobResult => JobResultColumns = {
    case j: PfaJobResult =>
      (j.jobId, j.node, j.timestamp, pfa_json, j.function, Some(j.model.compactPrint), None)
    case j: PfaExperimentJobResult =>
      (j.jobId,
       j.node,
       j.timestamp,
       pfa_experiment_json,
       j.function,
       Some(j.models.compactPrint),
       None)
    case j: ErrorJobResult =>
      (j.jobId, j.node, j.timestamp, pfa_json, j.function, None, Some(j.error))
    case j: JsonDataJobResult =>
      (j.jobId, j.node, j.timestamp, j.shape, j.function, Some(j.data.compactPrint), None)
    case j: OtherDataJobResult =>
      (j.jobId, j.node, j.timestamp, j.shape, j.function, Some(j.data), None)
  }

  implicit val JobResultComposite: Composite[JobResult] =
    Composite[JobResultColumns].imap(unsafeFromColumns)(jobResultToColumns)

  def queryJobResults(jobId: String): Query0[JobResult] =
    sql"select job_id, node, timestamp, shape, function, data, error from job_result where job_id = $jobId"
      .query[JobResult]

}

final class JobResultsDAL[M[_]: Monad] private (private val xa: Transactor[M]) extends DAL {
  import JobResultsDAO._
  import cats.implicits._
  import doobie._
  import doobie.implicits._

  def findJobResults(jobId: String): M[List[JobResult]] =
    queryJobResults(jobId).transact(xa)

}

object JobResultsDAL {
  import doobie._
  import cats.implicits._

  def apply[M[_]: Monad](xa: Transactor[M]): JobResultsDAL[M] = new JobResultsDAL(xa)
}

/*
class FederationDAL(xa: Transactor[IO]) extends JobResultsDAL {
{'fraction_non_alphanumeric': 0.11107547573230704, 'fraction_numerical': 0.002031216591832371, 'mean_word_length': 3.6588645418326693, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4247285', 'n_tokens_mistral': 2978, 'n_tokens_neox': 2919, 'n_words': 791}