package p.studio.compiler.pbs.parser;

import org.junit.jupiter.api.Test;
import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.lexer.PbsLexer;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;

import static org.junit.jupiter.api.Assertions.*;

class PbsBarrelParserTest {

    @Test
    void shouldParseBarrelShapeWithFunctionReturnSurfaces() {
        final var source = """
                pub fn sum(a: int, b: int) -> int;
                mod fn run(state: optional int) -> result<Err> (value: int, meta: float);
                pub struct Vec2;
                mod global Palette;
                pub callback Tick;
                """;

        final var diagnostics = DiagnosticSink.empty();
        final var fileId = new FileId(0);
        final var tokens = PbsLexer.lex(source, fileId, diagnostics);
        final var barrel = PbsBarrelParser.parse(tokens, fileId, diagnostics);

        assertTrue(diagnostics.isEmpty(), "Valid barrel should parse without diagnostics");
        assertEquals(5, barrel.items().size());

        final var sum = assertInstanceOf(PbsAst.BarrelFunctionItem.class, barrel.items().get(0));
        assertEquals("sum", sum.name());
        assertEquals(PbsAst.ReturnKind.PLAIN, sum.returnKind());
        assertEquals("int", sum.returnType().name());
        assertEquals(2, sum.parameterTypes().size());

        final var run = assertInstanceOf(PbsAst.BarrelFunctionItem.class, barrel.items().get(1));
        assertEquals(PbsAst.ReturnKind.RESULT, run.returnKind());
        assertEquals("Err", run.resultErrorType().name());
        assertEquals(PbsAst.TypeRefKind.NAMED_TUPLE, run.returnType().kind());

        assertInstanceOf(PbsAst.BarrelStructItem.class, barrel.items().get(2));
        assertInstanceOf(PbsAst.BarrelGlobalItem.class, barrel.items().get(3));
        assertInstanceOf(PbsAst.BarrelCallbackItem.class, barrel.items().get(4));
    }

    @Test
    void shouldRecoverFromInvalidBarrelItems() {
        final var source = """
                pub fn ok() -> int;
                pub type Invalid;
                mod struct Ready;
                """;

        final var diagnostics = DiagnosticSink.empty();
        final var fileId = new FileId(0);
        final var barrel = PbsBarrelParser.parse(PbsLexer.lex(source, fileId, diagnostics), fileId, diagnostics);

        assertTrue(diagnostics.hasErrors(), "Invalid barrel item should report diagnostics");
        assertEquals(2, barrel.items().size());
        assertInstanceOf(PbsAst.BarrelFunctionItem.class, barrel.items().get(0));
        assertInstanceOf(PbsAst.BarrelStructItem.class, barrel.items().get(1));
    }
}
