package p.studio.compiler.pbs.ast;

import p.studio.compiler.source.Span;
import p.studio.utilities.structures.ReadOnlyList;

import java.util.ArrayList;

public final class PbsAst {
    private PbsAst() {
    }

    public enum ReturnKind {
        INFERRED_UNIT,
        EXPLICIT_UNIT,
        PLAIN,
        RESULT
    }

    public enum TypeRefKind {
        SIMPLE,
        SELF,
        OPTIONAL,
        UNIT,
        GROUP,
        NAMED_TUPLE,
        ERROR
    }

    public record File(
            ReadOnlyList<ImportDecl> imports,
            ReadOnlyList<TopDecl> topDecls,
            Span span) {

        public ReadOnlyList<FunctionDecl> functions() {
            final var functions = new ArrayList<FunctionDecl>();
            for (final var topDecl : topDecls) {
                if (topDecl instanceof FunctionDecl functionDecl) {
                    functions.add(functionDecl);
                }
            }
            return ReadOnlyList.wrap(functions);
        }
    }

    public record ImportDecl(
            ReadOnlyList<ImportItem> items,
            ModuleRef moduleRef,
            Span span) {
    }

    public record ImportItem(
            String name,
            String alias,
            Span span) {
    }

    public record ModuleRef(
            String project,
            ReadOnlyList<String> pathSegments,
            Span span) {
    }

    public record BarrelFile(
            ReadOnlyList<BarrelItem> items,
            Span span) {
    }

    public record Attribute(
            String name,
            ReadOnlyList<AttributeArgument> arguments,
            Span span) {
    }

    public record AttributeArgument(
            String name,
            AttributeValue value,
            Span span) {
    }

    public sealed interface AttributeValue permits AttributeStringValue,
            AttributeIntValue,
            AttributeBoolValue,
            AttributeErrorValue {
        Span span();
    }

    public record AttributeStringValue(
            String value,
            Span span) implements AttributeValue {
    }

    public record AttributeIntValue(
            long value,
            Span span) implements AttributeValue {
    }

    public record AttributeBoolValue(
            boolean value,
            Span span) implements AttributeValue {
    }

    public record AttributeErrorValue(
            String value,
            Span span) implements AttributeValue {
    }

    public sealed interface TopDecl permits FunctionDecl,
            StructDecl,
            ContractDecl,
            HostDecl,
            BuiltinTypeDecl,
            ServiceDecl,
            ErrorDecl,
            EnumDecl,
            CallbackDecl,
            GlobalDecl,
            ConstDecl,
            ImplementsDecl,
            InvalidDecl {
        Span span();
    }

    public enum LifecycleMarker {
        NONE,
        INIT,
        FRAME
    }

    public record FunctionDecl(
            String name,
            ReadOnlyList<Parameter> parameters,
            ReturnKind returnKind,
            TypeRef returnType,
            TypeRef resultErrorType,
            LifecycleMarker lifecycleMarker,
            Block body,
            Span span) implements TopDecl {
    }

    public record StructDecl(
            String name,
            ReadOnlyList<StructField> fields,
            ReadOnlyList<FunctionDecl> methods,
            ReadOnlyList<CtorDecl> ctors,
            boolean hasBody,
            Span span) implements TopDecl {
    }

    public record ContractDecl(
            String name,
            ReadOnlyList<FunctionSignature> signatures,
            Span span) implements TopDecl {
    }

    public record HostDecl(
            String name,
            ReadOnlyList<FunctionSignature> signatures,
            Span span) implements TopDecl {
    }

    public record BuiltinTypeDecl(
            String name,
            ReadOnlyList<BuiltinFieldDecl> fields,
            ReadOnlyList<FunctionSignature> signatures,
            ReadOnlyList<Attribute> attributes,
            Span span) implements TopDecl {
    }

    public record ServiceDecl(
            String name,
            ReadOnlyList<FunctionDecl> methods,
            Span span) implements TopDecl {
    }

    public record ErrorDecl(
            String name,
            ReadOnlyList<String> cases,
            Span span) implements TopDecl {
    }

    public record EnumDecl(
            String name,
            ReadOnlyList<EnumCase> cases,
            Span span) implements TopDecl {
    }

    public record CallbackDecl(
            String name,
            ReadOnlyList<Parameter> parameters,
            ReturnKind returnKind,
            TypeRef returnType,
            TypeRef resultErrorType,
            Span span) implements TopDecl {
    }

    public record GlobalDecl(
            String name,
            TypeRef explicitType,
            Expression initializer,
            Span span) implements TopDecl {
    }

    public record ConstDecl(
            String name,
            TypeRef explicitType,
            Expression initializer,
            ReadOnlyList<Attribute> attributes,
            Span span) implements TopDecl {
    }

    public record ImplementsDecl(
            String contractName,
            String ownerName,
            String binderName,
            ReadOnlyList<FunctionDecl> methods,
            Span span) implements TopDecl {
    }

    public record InvalidDecl(
            String reason,
            Span span) implements TopDecl {
    }

    public record Parameter(
            String name,
            TypeRef typeRef,
            Span span) {
    }

    public record CtorDecl(
            String name,
            ReadOnlyList<Parameter> parameters,
            Block body,
            Span span) {
    }

    public record TypeRef(
            TypeRefKind kind,
            String name,
            ReadOnlyList<NamedTypeField> fields,
            TypeRef inner,
            Span span) {
        public static TypeRef simple(final String name, final Span span) {
            return new TypeRef(TypeRefKind.SIMPLE, name, ReadOnlyList.empty(), null, span);
        }

        public static TypeRef self(final Span span) {
            return new TypeRef(TypeRefKind.SELF, "Self", ReadOnlyList.empty(), null, span);
        }

        public static TypeRef optional(final TypeRef inner, final Span span) {
            return new TypeRef(TypeRefKind.OPTIONAL, "optional", ReadOnlyList.empty(), inner, span);
        }

        public static TypeRef unit(final Span span) {
            return new TypeRef(TypeRefKind.UNIT, "void", ReadOnlyList.empty(), null, span);
        }

        public static TypeRef group(final TypeRef inner, final Span span) {
            return new TypeRef(TypeRefKind.GROUP, "group", ReadOnlyList.empty(), inner, span);
        }

        public static TypeRef namedTuple(final ReadOnlyList<NamedTypeField> fields, final Span span) {
            return new TypeRef(TypeRefKind.NAMED_TUPLE, "tuple", fields, null, span);
        }

        public static TypeRef error(final Span span) {
            return new TypeRef(TypeRefKind.ERROR, "<error>", ReadOnlyList.empty(), null, span);
        }
    }

    public record NamedTypeField(
            String label,
            TypeRef typeRef,
            Span span) {
    }

    public record StructField(
            String name,
            TypeRef typeRef,
            boolean isPublic,
            boolean isMutable,
            Span span) {
    }

    public record BuiltinFieldDecl(
            String name,
            TypeRef typeRef,
            boolean isPublic,
            ReadOnlyList<Attribute> attributes,
            Span span) {
    }

    public record FunctionSignature(
            String name,
            ReadOnlyList<Parameter> parameters,
            ReturnKind returnKind,
            TypeRef returnType,
            TypeRef resultErrorType,
            ReadOnlyList<Attribute> attributes,
            Span span) {
    }

    public record EnumCase(
            String name,
            Long explicitValue,
            Span span) {
    }

    public enum AssignOperator {
        ASSIGN,
        ADD_ASSIGN,
        SUB_ASSIGN,
        MUL_ASSIGN,
        DIV_ASSIGN,
        MOD_ASSIGN
    }

    public sealed interface Statement permits LetStatement,
            AssignStatement,
            ReturnStatement,
            IfStatement,
            ForStatement,
            WhileStatement,
            BreakStatement,
            ContinueStatement,
            ExpressionStatement {
        Span span();
    }

    public record Block(
            ReadOnlyList<Statement> statements,
            Expression tailExpression,
            Span span) {
    }

    public record LValue(
            String rootName,
            ReadOnlyList<String> pathSegments,
            Span span) {
    }

    public record LetStatement(
            boolean isConst,
            String name,
            Span nameSpan,
            TypeRef explicitType,
            Expression initializer,
            Span span) implements Statement {
    }

    public record AssignStatement(
            LValue target,
            AssignOperator operator,
            Expression value,
            Span span) implements Statement {
    }

    public record ReturnStatement(
            Expression value,
            Span span) implements Statement {
    }

    public record IfStatement(
            Expression condition,
            Block thenBlock,
            IfStatement elseIf,
            Block elseBlock,
            Span span) implements Statement {
    }

    public record ForStatement(
            String iteratorName,
            TypeRef iteratorType,
            Expression fromExpression,
            Expression untilExpression,
            Expression stepExpression,
            Block body,
            Span span) implements Statement {
    }

    public record WhileStatement(
            Expression condition,
            Block body,
            Span span) implements Statement {
    }

    public record BreakStatement(
            Span span) implements Statement {
    }

    public record ContinueStatement(
            Span span) implements Statement {
    }

    public record ExpressionStatement(
            Expression expression,
            Span span) implements Statement {
    }

    public sealed interface Expression permits IdentifierExpr,
            IntLiteralExpr,
            FloatLiteralExpr,
            BoundedLiteralExpr,
            StringLiteralExpr,
            BoolLiteralExpr,
            UnaryExpr,
            BinaryExpr,
            ApplyExpr,
            ElseExpr,
            IfExpr,
            SwitchExpr,
            HandleExpr,
            AsExpr,
            CallExpr,
            MemberExpr,
            PropagateExpr,
            GroupExpr,
            ThisExpr,
            NewExpr,
            BindExpr,
            SomeExpr,
            NoneExpr,
            OkExpr,
            ErrExpr,
            UnitExpr,
            TupleExpr,
            BlockExpr {
        Span span();
    }

    public record IdentifierExpr(
            String name,
            Span span) implements Expression {
    }

    public record IntLiteralExpr(
            long value,
            Span span) implements Expression {
    }

    public record FloatLiteralExpr(
            double value,
            Span span) implements Expression {
    }

    public record BoundedLiteralExpr(
            int value,
            Span span) implements Expression {
    }

    public record StringLiteralExpr(
            String value,
            Span span) implements Expression {
    }

    public record BoolLiteralExpr(
            boolean value,
            Span span) implements Expression {
    }

    public record UnaryExpr(
            String operator,
            Expression expression,
            Span span) implements Expression {
    }

    public record BinaryExpr(
            String operator,
            Expression left,
            Expression right,
            Span span) implements Expression {
    }

    public record ApplyExpr(
            Expression callee,
            Expression argument,
            Span span) implements Expression {
    }

    public record ElseExpr(
            Expression optionalExpression,
            Expression fallbackExpression,
            Span span) implements Expression {
    }

    public record IfExpr(
            Expression condition,
            Block thenBlock,
            Expression elseExpression,
            Span span) implements Expression {
    }

    public sealed interface SwitchPattern permits WildcardSwitchPattern,
            LiteralSwitchPattern,
            EnumCaseSwitchPattern {
        Span span();
    }

    public enum WildcardSwitchKind {
        DEFAULT,
        UNDERSCORE,
        RECOVERY
    }

    public record WildcardSwitchPattern(
            WildcardSwitchKind kind,
            Span span) implements SwitchPattern {
    }

    public record LiteralSwitchPattern(
            Expression literal,
            Span span) implements SwitchPattern {
    }

    public record EnumCaseSwitchPattern(
            ErrorPath path,
            Span span) implements SwitchPattern {
    }

    public record SwitchArm(
            SwitchPattern pattern,
            Block block,
            Span span) {
    }

    public record SwitchExpr(
            Expression selector,
            ReadOnlyList<SwitchArm> arms,
            Span span) implements Expression {
    }

    public sealed interface HandlePattern permits WildcardHandlePattern, ErrorPathHandlePattern {
        Span span();
    }

    public record WildcardHandlePattern(
            Span span) implements HandlePattern {
    }

    public record ErrorPathHandlePattern(
            ErrorPath path,
            Span span) implements HandlePattern {
    }

    public record HandleArm(
            HandlePattern pattern,
            ErrorPath remapTarget,
            Block block,
            Span span) {
    }

    public record HandleExpr(
            Expression value,
            ReadOnlyList<HandleArm> arms,
            Span span) implements Expression {
    }

    public record AsExpr(
            Expression expression,
            String contractName,
            Span span) implements Expression {
    }

    public record CallExpr(
            Expression callee,
            ReadOnlyList<Expression> arguments,
            Span span) implements Expression {
    }

    public record MemberExpr(
            Expression receiver,
            String memberName,
            Span span) implements Expression {
    }

    public record PropagateExpr(
            Expression expression,
            Span span) implements Expression {
    }

    public record GroupExpr(
            Expression expression,
            Span span) implements Expression {
    }

    public record ThisExpr(
            Span span) implements Expression {
    }

    public record NewExpr(
            String typeName,
            String ctorName,
            ReadOnlyList<Expression> arguments,
            Span span) implements Expression {
    }

    public record BindExpr(
            Expression contextExpression,
            String functionName,
            Span span) implements Expression {
    }

    public record SomeExpr(
            Expression value,
            Span span) implements Expression {
    }

    public record NoneExpr(
            Span span) implements Expression {
    }

    public record OkExpr(
            Expression value,
            Span span) implements Expression {
    }

    public record ErrExpr(
            ErrorPath errorPath,
            Span span) implements Expression {
    }

    public record UnitExpr(
            Span span) implements Expression {
    }

    public record TupleItem(
            String label,
            Expression expression,
            Span span) {
    }

    public record TupleExpr(
            ReadOnlyList<TupleItem> items,
            Span span) implements Expression {
    }

    public record BlockExpr(
            Block block,
            Span span) implements Expression {
    }

    public record ErrorPath(
            ReadOnlyList<String> segments,
            Span span) {
    }

    // Barrel declarations are represented explicitly for linking-visible declaration families.
    public sealed interface BarrelItem permits BarrelFunctionItem,
            BarrelStructItem,
            BarrelContractItem,
            BarrelHostItem,
            BarrelErrorItem,
            BarrelEnumItem,
            BarrelServiceItem,
            BarrelGlobalItem,
            BarrelConstItem,
            BarrelCallbackItem {
        Span span();
    }

    public enum Visibility {
        MOD,
        PUB
    }

    public record BarrelFunctionItem(
            Visibility visibility,
            String name,
            ReadOnlyList<TypeRef> parameterTypes,
            ReturnKind returnKind,
            TypeRef returnType,
            TypeRef resultErrorType,
            Span span) implements BarrelItem {
    }

    public record BarrelStructItem(
            Visibility visibility,
            String name,
            Span span) implements BarrelItem {
    }

    public record BarrelContractItem(
            Visibility visibility,
            String name,
            Span span) implements BarrelItem {
    }

    public record BarrelHostItem(
            Visibility visibility,
            String name,
            Span span) implements BarrelItem {
    }

    public record BarrelErrorItem(
            Visibility visibility,
            String name,
            Span span) implements BarrelItem {
    }

    public record BarrelEnumItem(
            Visibility visibility,
            String name,
            Span span) implements BarrelItem {
    }

    public record BarrelServiceItem(
            Visibility visibility,
            String name,
            Span span) implements BarrelItem {
    }

    public record BarrelGlobalItem(
            Visibility visibility,
            String name,
            Span span) implements BarrelItem {
    }

    public record BarrelConstItem(
            Visibility visibility,
            String name,
            Span span) implements BarrelItem {
    }

    public record BarrelCallbackItem(
            Visibility visibility,
            String name,
            Span span) implements BarrelItem {
    }
}
