package fr.rolandl.mymovies.service

import fr.rolandl.mymovies.bo.Database
import fr.rolandl.mymovies.bo.Film
import fr.rolandl.mymovies.bo.Person
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File

object MovieService
  : IMovieService
{

  private var database: Database? = null

  override suspend fun loadDatabase()
  {
    database = try
    {
      val json = File("database.json").readText()
      Json.decodeFromString(json)
    }
    catch (exception: Exception)
    {
      Database()
    }
  }

  override suspend fun saveDatabase(): Boolean
  {
    return try
    {
      val json = Json.encodeToString(database)
      File("database.json").writeText(json)
      true
    }
    catch (exception: Exception)
    {
      false
    }
  }

  override suspend fun addFilm(film: Film)
  {
    //we check the director
    getPersonById(film.directedBy) ?: throw IllegalArgumentException("Unknown director")

    //we check the actors
    film.actors.forEach {
      getPersonById(it) ?: throw IllegalArgumentException("Unknown actor")
    }

    //we add the film
    database?.films?.add(film)
  }

  override suspend fun addPerson(person: Person)
  {
    database?.persons?.add(person)
  }

  override suspend fun getFilmById(id: Int): Film? =
      database?.films?.firstOrNull { it.id == id }

  override suspend fun getPersonById(id: Int): Person? =
      database?.persons?.firstOrNull { it.id == id }

  //...

  private suspend fun updateFilmData(film: Film): Film?
  {
    val actors = film.actors.mapNotNull { getPersonById(it) }
    val director = getPersonById(film.directedBy)

    return film.copy(directorPerson = director, actorsPerson = actors)
  }

  override suspend fun getFilmByName(name: String): Film?
  {
    val film = database?.films?.firstOrNull { it.title.equals(name, true) }
    return if (film == null) film else updateFilmData(film)
  }

  //..

  override suspend fun getFilmByActorId(id: Int): List<Film> =
      database?.films?.filter { it.actors.contains(id) }?.mapNotNull { updateFilmData(it) } ?: listOf()

  override suspend fun getFilmByDirectorId(id: Int): List<Film> =
      database?.films?.filter { it.directedBy == id }?.mapNotNull { updateFilmData(it) } ?: listOf()

  override suspend fun getFilmByGenre(genre: Film.Genre): List<Film> =
      database?.films?.filter { it.genres.contains(genre) }?.mapNotNull { updateFilmData(it) } ?: listOf()

  override suspend fun getAllPersons(): List<Person> =
      database?.persons ?: listOf()

  override suspend fun getAllGenres(): List<Film.Genre> =
      Film.Genre.values().toList()

}