package p.studio.compiler.pbs.semantics;

import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.source.diagnostics.DiagnosticSink;

import java.util.ArrayList;
import java.util.List;

final class PbsTypeSurfaceSemanticsValidator {
    private PbsTypeSurfaceSemanticsValidator() {
    }

    static void validateTypeSurfaceRecursive(
            final List<PbsAst.TypeRef> roots,
            final String ownerDescription,
            final boolean allowSelf,
            final DiagnosticSink diagnostics) {
        final var pending = new ArrayList<>(roots);
        while (!pending.isEmpty()) {
            final var typeRef = pending.removeLast();
            if (typeRef == null) {
                continue;
            }

            switch (typeRef.kind()) {
                case OPTIONAL -> {
                    if (typeRef.inner() == null || typeRef.inner().kind() == PbsAst.TypeRefKind.UNIT) {
                        p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, 
                                PbsSemanticsErrors.E_SEM_INVALID_OPTIONAL_VOID_TYPE_SURFACE.name(),
                                "Invalid optional type surface in %s: 'optional void' is not allowed".formatted(ownerDescription),
                                typeRef.span());
                    }
                    pending.add(typeRef.inner());
                }
                case GROUP -> pending.add(typeRef.inner());
                case NAMED_TUPLE -> {
                    for (final var field : typeRef.fields()) {
                        pending.add(field.typeRef());
                    }
                }
                case SELF -> {
                    if (!allowSelf) {
                        p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
                                PbsSemanticsErrors.E_SEM_INVALID_SELF_CONTEXT.name(),
                                "Invalid 'Self' type usage in %s".formatted(ownerDescription),
                                typeRef.span());
                    }
                }
                default -> {
                    // No recursive children.
                }
            }
        }
    }

    static boolean isOptionalType(final PbsAst.TypeRef typeRef) {
        if (typeRef == null) {
            return false;
        }
        if (typeRef.kind() == PbsAst.TypeRefKind.OPTIONAL) {
            return true;
        }
        if (typeRef.kind() == PbsAst.TypeRefKind.GROUP) {
            return isOptionalType(typeRef.inner());
        }
        return false;
    }
}
