@file:Suppress("DEPRECATION" /* will be removed with 1.0.0 */)

package ch.tutteli.atrium.domain.robstoll.lib.creating.collectors

import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.assertions.builders.withExplanatoryAssertion
import ch.tutteli.atrium.creating.BaseAssertionPlant
import ch.tutteli.atrium.creating.BaseCollectingAssertionPlant
import ch.tutteli.atrium.creating.MaybeSubject
import ch.tutteli.atrium.creating.PlantHasNoSubjectException
import ch.tutteli.atrium.domain.builders.AssertImpl
import ch.tutteli.atrium.reporting.Text
import ch.tutteli.atrium.reporting.translating.Translatable
import ch.tutteli.atrium.translations.DescriptionBasic

@Deprecated("Switch from Assert to Expect and use _collectForComposition instead; will be removed with 1.0.0")
class AssertionCollectorForExplanationImpl<T, A : BaseAssertionPlant<T, A>, C : BaseCollectingAssertionPlant<T, A, C>>(
    private val throwIfNoAssertionIsCollected: Boolean,
    private val collectingPlantFactory: (() -> T) -> C
) {

    fun collect(
        warningCannotEvaluate: Translatable,
        subject: MaybeSubject<T>,
        assertionCreator: (C.() -> Unit)?
    ): List<Assertion> {
        return try {
            val collectedAssertions = collect(subject, assertionCreator)

            require(!(throwIfNoAssertionIsCollected && collectedAssertions.isEmpty())) {
                "There was not any assertion created. Specify at least one assertion"
            }

            // since assertions can be lazily computed we have to provoke their creation here,
            // so that a potential PlantHasNoSubjectException is thrown. It's fine to provoke the computation
            // because we require the assertions for the explanation anyway.
            expandAssertionGroups(collectedAssertions)

            collectedAssertions
        } catch (e: PlantHasNoSubjectException) {
            listOf(
                AssertImpl.builder.explanatoryGroup
                    .withWarningType
                    .withExplanatoryAssertion(warningCannotEvaluate)
                    .build()
            )
        }
    }

    private fun collect(@Suppress("DEPRECATION") subject: MaybeSubject<T>, assertionCreator: (C.() -> Unit)?): List<Assertion> {
        //TODO almost same as in _containsKeyWithNullableValueAssertions
        return if (assertionCreator != null) {
            val collectingAssertionPlant = collectingPlantFactory(subject::get)
            collectingAssertionPlant.assertionCreator()
            collectingAssertionPlant.getAssertions()
        } else {
            listOf(AssertImpl.builder.createDescriptive(DescriptionBasic.IS, Text.NULL) {
                subject is MaybeSubject.Absent
            })
        }
    }
}
