/*
 * Copyright (c) 2016-2018 original author or authors
 *
 * 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 controllers.handlers

import java.util.UUID

import akka.actor.ActorRef
import akka.pattern.ask
import akka.util.Timeout
import io.heta.tap.analysis.batch.AwsS3Client
import io.heta.tap.analysis.batch.BatchActor.{AnalyseSource, CheckProgress, INIT, ResultMessage}
import io.heta.tap.data.results.BatchResult
import javax.inject.{Inject, Named}
import play.api.Logger

import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.Success


/* Possible parameters
analysisType: any valid pipeline query name
s3bucket: any valid s3 bucket name which TAP has been given access to
progressCheck: batchId [UUID]
 */


class BatchAnalysisHandler @Inject()(awsS3Client: AwsS3Client, @Named("batch")batch:ActorRef, @Named("cluAnnotator")cluAnnotator:ActorRef) extends GenericHandler {

  import io.heta.tap.pipelines.materialize.PipelineContext.executor

  val logger: Logger = Logger(this.getClass)

  {
    implicit val timeout: Timeout = 5.seconds
    (batch ? INIT).onComplete{
      case Success(result:Boolean) => if(result) {
        logger.info("BatchActor initialised successfully")
      } else {
        logger.error("There was a problem initialising the BatchActor")
      }
      case scala.util.Failure(exception) => logger.error(exception.getMessage)
    }
  }

  def analyse(parameters:Option[String],start:Long):Future[BatchResult] = {
    logger.info(s"Batch analysis with parameters: $parameters")
    process(parameters) match {
      case Right(futResMsg:Future[ResultMessage]) => futResMsg.map( rm => BatchResult(rm.result,rm.message,(System.currentTimeMillis() - start).toInt))
      case Left(error) => Future(BatchResult("",error.getMessage,(System.currentTimeMillis() - start).toInt))
    }
  }

  def process(parameters:Option[String]): Either[Throwable,Future[ResultMessage]] = try {
    val bucket = extractParameter[String]("s3bucket",parameters)
    if(bucket.nonEmpty) {
      val analysis = extractParameter[String]("analysisType",parameters)
      if (analysis.nonEmpty) sendRequest(ANALYSE,bucket.get.toString,analysis.get.toString)
      else {
        val progress = extractParameter[String]("progressCheck",parameters)
        if (progress.nonEmpty) {
          val batchId = progress.getOrElse("").asInstanceOf[String]
          val uuid = UUID.fromString(batchId)
          sendRequest(PROGRESS,bucket.get.toString,uuid.toString)
        }
        else throw new Exception("ERROR: No valid parameters provided")
      }
    } else throw new Exception("ERROR: No S3 bucket provided")
  } catch {
    case error => Left(error)
  }

  def sendRequest(requestType:RequestType,bucket:String,payLoad:String):Either[Throwable,Future[ResultMessage]] = {
    implicit val timeout: Timeout = 5.seconds

        val request = requestType match {
          case ANALYSE => AnalyseSource(bucket,payLoad,cluAnnotator)
          case PROGRESS => CheckProgress(bucket,payLoad)
        }
        val result = (batch ? request).mapTo[Future[ResultMessage]].flatten
        Right(result)
     // }
     // case None => throw new Exception("No S3 Client Found.")
    //}
  }

  type RequestType = String
  lazy val ANALYSE:RequestType = "analyse"
  lazy val PROGRESS:RequestType = "progress"
}