/*
 * Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
 *
 * 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 net.sergeych.lyng.obj

import kotlinx.coroutines.Deferred
import kotlin.coroutines.cancellation.CancellationException as KotlinCancellationException
import net.sergeych.lyng.Scope
import net.sergeych.lyng.miniast.addFnDoc
import net.sergeych.lyng.miniast.addPropertyDoc
import net.sergeych.lyng.miniast.type

open class ObjDeferred(val deferred: Deferred<Obj>): Obj() {

    override val objClass get() = type

    companion object {
        val type = object: ObjClass("Deferred"){
            override suspend fun callOn(scope: Scope): Obj {
                scope.raiseError("Deferred constructor is not directly callable")
            }
        }.apply {
            addFnDoc(
                name = "cancel",
                doc = "Cancel this deferred if it is still active. Safe to call multiple times.",
                returns = type("lyng.Void"),
                moduleName = "lyng.stdlib"
            ) {
                thisAs<ObjDeferred>().deferred.cancel()
                ObjVoid
            }
            addFnDoc(
                name = "await",
                doc = "Suspend until completion and return the result value. Throws `CancellationException` if cancelled.",
                returns = type("lyng.Any"),
                moduleName = "lyng.stdlib"
            ) {
                try {
                    thisAs<ObjDeferred>().deferred.await()
                } catch (e: KotlinCancellationException) {
                    requireScope().raiseError(ObjCancellationException(requireScope(), e.message ?: "Deferred was cancelled"))
                }
            }
            addPropertyDoc(
                name = "isCompleted",
                doc = "Whether this deferred has completed (successfully or with an error).",
                type = type("lyng.Bool"),
                moduleName = "lyng.stdlib",
                getter = { thisAs<ObjDeferred>().deferred.isCompleted.toObj() }
            )
            addPropertyDoc(
                name = "isActive",
                doc = "Whether this deferred is currently active (not completed and not cancelled).",
                type = type("lyng.Bool"),
                moduleName = "lyng.stdlib",
                getter = {
                    val d = thisAs<ObjDeferred>().deferred
                    (d.isActive || (!d.isCompleted && !d.isCancelled)).toObj()
                }
            )
            addPropertyDoc(
                name = "isCancelled",
                doc = "Whether this deferred was cancelled.",
                type = type("lyng.Bool"),
                moduleName = "lyng.stdlib",
                getter = { thisAs<ObjDeferred>().deferred.isCancelled.toObj() }
            )
        }
    }
}
