package services

import com.google.inject.Inject
import com.mohiva.play.silhouette.api.LoginInfo
import com.ysoft.odc.SetDiff
import controllers.{ProjectsWithReports, ReportInfo}
import models._
import play.api.db.slick.{HasDatabaseConfigProvider, DatabaseConfigProvider}

import scala.collection.immutable.Iterable
import scala.concurrent.{Future, ExecutionContext}

class VulnerabilityNotificationService @Inject() (protected val dbConfigProvider: DatabaseConfigProvider)(implicit executionContext: ExecutionContext) extends HasDatabaseConfigProvider[models.profile.type]{
  import dbConfig.driver.api._

  import models.tables
  import models.tables.vulnerabilitySubscriptions

  def watchedProjectsByUser(identity: LoginInfo) = db.run(vulnerabilitySubscriptions.filter(_.user === identity).result)
  def subscribe(user: LoginInfo, project: String) = db.run(vulnerabilitySubscriptions += VulnerabilitySubscription(user = user, project = project))

  def unsubscribe(user: LoginInfo, project: String) = db.run(vulnerabilitySubscriptions.filter(vs => vs.user === user && vs.project === project).delete)

  def getRecipientsForProjects(projects: Set[ReportInfo]) = {
    val bareProjects = projects.map(_.bare)
    val expandedProjects = projects ++ bareProjects
    val relevantFullIds = expandedProjects.map(_.fullId)
    db.run(vulnerabilitySubscriptions.filter(_.project inSet relevantFullIds).map(_.user()).result)
  }

  class ExportPlatform[T, U] private[VulnerabilityNotificationService] (ept: ExportPlatformTables[T, U]) {
    def changeProjects(ticketId: Int, diff: SetDiff[String], projects: ProjectsWithReports) = db.run(
      DBIO.seq(
        ept.projects.filter(_.exportedVulnerabilityId === ticketId).delete,
        ept.projects ++= diff.newSet.map(fullId => ExportedVulnerabilityProject(ticketId, fullId)).toSet
      ).transactionally
    )

    def projectsForTickets(ticketsIds: Set[Int]): Future[Map[Int, Set[String]]] = db.run(
      ept.projects.filter(_.exportedVulnerabilityId inSet ticketsIds).result
    ).map{_.groupBy(_.exportedVulnerabilityId).mapValues(_.map(_.projectFullId).toSet).map(identity).withDefaultValue(Set())}

    def ticketsForVulnerabilities(vulnerabilities: Traversable[String]) = db.run(
      ept.tickets.filter(_.vulnerabilityName inSet vulnerabilities).result
    ).map(_.map{ rec =>
        rec._2.vulnerabilityName -> rec
    }.toMap)

    def ticketForVulnerability(vulnerabilityName: String) = db.run(
      ept.tickets.filter(_.vulnerabilityName === vulnerabilityName).map(_.base).result
    ).map(_.headOption)


    def addTicket(vulnerabilityTicket: ExportedVulnerability[T], projects: Set[ReportInfo]): Future[Any] = db.run(
      (
        ept.tickets.map(_.base).returning(ept.tickets.map(_.id)) += vulnerabilityTicket
      ).flatMap( id =>
        ept.projects ++= projects.map(ri => ExportedVulnerabilityProject(id, ri.fullId)).toSet
      ).transactionally
    )

  }

  val issueTrackerExport = new ExportPlatform(tables.issueTrackerExportTables)

  val mailExport = new ExportPlatform(tables.mailExportTables)

}
