/**
 * Symbolic Evaluation Test File - Scala
 *
 * This file contains multiple source→sink flows for testing symbolic evaluation.
 * Each section demonstrates dangerous (unsafe) and safe patterns.
 *
 * Patterns covered:
 * - SQL injection (string interpolation vs prepared statements)
 * - Command injection (Process, sys.process)
 * - Play Framework request handling
 * - Pattern matching
 * - Option/Either handling
 * - Case classes and extractors
 */

package com.example.symbolic

import java.sql.{Connection, PreparedStatement, ResultSet}
import scala.sys.process._
import scala.io.Source
import play.api.mvc._
import play.api.libs.json._

// =============================================================================
// DATA MODELS
// =============================================================================

case class User(id: String, username: String, email: String)

object User {
  implicit val format: Format[User] = Json.format[User]
}

// =============================================================================
// SQL INJECTION PATTERNS
// =============================================================================

class UserRepository(connection: Connection) {

  // UNSAFE: String interpolation with user input
  def getUserUnsafeInterpolation(userId: String): Option[User] = {
    val query = s"SELECT * FROM users WHERE id = '$userId'"
    val stmt = connection.createStatement()
    val rs = stmt.executeQuery(query)  // Sink: danger
    mapUser(rs)
  }

  // UNSAFE: String concatenation
  def getUserUnsafeConcat(username: String): Option[User] = {
    val query = "SELECT * FROM users WHERE username = '" + username + "'"
    val stmt = connection.createStatement()
    val rs = stmt.executeQuery(query)  // Sink: danger
    mapUser(rs)
  }

  // UNSAFE: Format string
  def getUserUnsafeFormat(email: String): Option[User] = {
    val query = "SELECT * FROM users WHERE email = '%s'".format(email)
    val stmt = connection.createStatement()
    val rs = stmt.executeQuery(query)  // Sink: danger
    mapUser(rs)
  }

  // SAFE: PreparedStatement with parameters
  def getUserSafePrepared(userId: String): Option[User] = {
    val query = "SELECT * FROM users WHERE id = ?"
    val stmt = connection.prepareStatement(query)
    stmt.setString(1, userId)  // Safe: parameterized
    val rs = stmt.executeQuery()
    mapUser(rs)
  }

  // SAFE: Constant query
  def getAllUsersSafe(): List[User] = {
    val query = "SELECT * FROM users"
    val stmt = connection.createStatement()
    val rs = stmt.executeQuery(query)  // Safe: constant
    mapUsers(rs)
  }

  private def mapUser(rs: ResultSet): Option[User] = {
    if (rs.next()) Some(User(rs.getString("id"), rs.getString("username"), rs.getString("email")))
    else None
  }

  private def mapUsers(rs: ResultSet): List[User] = {
    var users = List.empty[User]
    while (rs.next()) {
      users = users :+ User(rs.getString("id"), rs.getString("username"), rs.getString("email"))
    }
    users
  }
}

// =============================================================================
// COMMAND INJECTION PATTERNS
// =============================================================================

class CommandService {

  // UNSAFE: Process with interpolated command
  def executeUnsafeProcess(filename: String): String = {
    val command = s"cat $filename"
    command.!!  // Sink: danger (sys.process interpolation)
  }

  // UNSAFE: Seq process with shell
  def executeUnsafeSeq(path: String): String = {
    val command = Seq("sh", "-c", s"ls $path")
    command.!!  // Sink: danger
  }

  // UNSAFE: ProcessBuilder with concatenation
  def executeUnsafeBuilder(filename: String): String = {
    import scala.sys.process.Process
    val command = "cat " + filename
    Process(command).!!  // Sink: danger
  }

  // SAFE: Seq process without shell interpretation
  def executeSafeSeq(filename: String): String = {
    val command = Seq("cat", filename)
    command.!!  // Safer: no shell
  }

  // SAFE: Constant command
  def listDirectorySafe(): String = {
    "ls -la".!!  // Safe: constant
  }
}

// =============================================================================
// FILE OPERATIONS PATTERNS
// =============================================================================

class FileService(basePath: String) {

  // UNSAFE: Interpolated path
  def readFileUnsafeInterpolation(filename: String): String = {
    val path = s"$basePath/$filename"
    Source.fromFile(path).mkString  // Sink: path traversal
  }

  // UNSAFE: Concatenated path
  def readFileUnsafeConcat(filename: String): String = {
    val path = basePath + "/" + filename
    Source.fromFile(path).mkString  // Sink: danger
  }

  // SAFE: Validated path
  def readFileSafe(filename: String): String = {
    require(!filename.contains("..") && !filename.startsWith("/"), "Invalid filename")
    val path = s"$basePath/$filename"
    Source.fromFile(path).mkString  // Safe after validation
  }
}

// =============================================================================
// PATTERN MATCHING PATTERNS
// =============================================================================

class PatternMatchService {

  // UNSAFE: Pattern match extracts dangerous value
  def processMatchUnsafe(input: Option[String]): String = {
    input match {
      case Some(value) => value  // Danger from extracted value
      case None => "default"
    }
  }

  // UNSAFE: Case class extractor
  def processUserUnsafe(user: User): String = {
    user match {
      case User(id, username, _) => username  // Danger from extracted field
    }
  }

  // UNSAFE: Guard doesn't sanitize
  def processWithGuardUnsafe(input: String): String = {
    input match {
      case s if s.nonEmpty => s  // Guard doesn't sanitize, still dangerous
      case _ => "default"
    }
  }

  // SAFE: All cases return constants
  def processMatchSafe(code: Int): String = {
    code match {
      case 1 => "option_a"
      case 2 => "option_b"
      case _ => "default"
    }
  }
}

// =============================================================================
// OPTION/EITHER HANDLING
// =============================================================================

class FunctionalService {

  // UNSAFE: map propagates danger
  def processOptionUnsafe(input: Option[String]): Option[String] = {
    input.map(_.toUpperCase)  // Danger propagates through map
  }

  // UNSAFE: flatMap propagates danger
  def processFlatMapUnsafe(input: Option[String]): Option[String] = {
    input.flatMap(s => Some(s.trim))  // Danger propagates
  }

  // UNSAFE: getOrElse with danger
  def processGetOrElseUnsafe(input: Option[String]): String = {
    input.getOrElse("default")  // Danger if Some
  }

  // UNSAFE: fold with danger in non-empty case
  def processFoldUnsafe(input: Option[String]): String = {
    input.fold("default")(identity)  // Danger in non-empty case
  }

  // UNSAFE: Either right projection
  def processEitherUnsafe(input: Either[String, String]): String = {
    input.getOrElse("default")  // Danger if Right
  }

  // SAFE: Constant transformation
  def processOptionSafe(input: Option[String]): Option[String] = {
    input.map(_ => "constant")  // Transforms to constant
  }
}

// =============================================================================
// VARIABLE PROPAGATION
// =============================================================================

class DataProcessor {

  // UNSAFE: Val assignment propagates
  def processUnsafe(input: String): String = {
    val processed = input  // Assignment propagates
    val transformed = processed.toUpperCase  // Method on dangerous
    transformed
  }

  // UNSAFE: Multiple vals
  def chainAssignmentUnsafe(value: String): String = {
    val a = value
    val b = a
    val c = b
    c  // Danger propagates
  }

  // SAFE: Reassignment not possible with val, but new binding is safe
  def processSafe(input: String): String = {
    val processed = "constant_value"  // New constant
    processed
  }
}

// =============================================================================
// PLAY FRAMEWORK CONTROLLER (Sources)
// =============================================================================

class UserController(
  cc: ControllerComponents,
  userRepository: UserRepository,
  commandService: CommandService,
  fileService: FileService
) extends AbstractController(cc) {

  // UNSAFE: Query parameter to SQL
  def getUserUnsafe(): Action[AnyContent] = Action { request =>
    val userId = request.getQueryString("id").getOrElse("")  // Source
    val user = userRepository.getUserUnsafeInterpolation(userId)  // Danger
    user.map(u => Ok(Json.toJson(u))).getOrElse(NotFound)
  }

  // SAFE: Parameterized query
  def getUserSafe(): Action[AnyContent] = Action { request =>
    val userId = request.getQueryString("id").getOrElse("")  // Source
    val user = userRepository.getUserSafePrepared(userId)  // Safe
    user.map(u => Ok(Json.toJson(u))).getOrElse(NotFound)
  }

  // UNSAFE: Path parameter to command
  def execUnsafe(filename: String): Action[AnyContent] = Action {
    val result = commandService.executeUnsafeProcess(filename)  // Danger
    Ok(result)
  }

  // UNSAFE: Body to file read
  def readFileUnsafe(): Action[JsValue] = Action(parse.json) { request =>
    val filename = (request.body \ "filename").as[String]  // Source
    val content = fileService.readFileUnsafeInterpolation(filename)  // Danger
    Ok(content)
  }
}

// =============================================================================
// CONTROL FLOW PATTERNS
// =============================================================================

class ControlFlowService {

  // UNSAFE: If expression with danger
  def conditionalUnsafe(userInput: String): String = {
    if (userInput.nonEmpty) userInput else "default"  // Danger in true case
  }

  // UNSAFE: For comprehension propagates danger
  def forComprehensionUnsafe(inputs: List[String]): List[String] = {
    for {
      input <- inputs
      processed = input.toUpperCase  // Danger propagates
    } yield processed
  }

  // SAFE: Both branches are constants
  def conditionalSafe(flag: Boolean): String = {
    if (flag) "option_a" else "option_b"  // Both safe
  }
}
