/**
 * Class declaration conversion orchestrator
 */
import * as ts from "typescript";
import { hasExportModifier, convertTypeParameters, hasDeclareModifier, } from "../../helpers.js";
import { convertAccessorProperty, convertProperty } from "./properties.js";
import { convertMethod } from "./methods.js";
import { convertConstructor, extractParameterProperties, } from "./constructors.js";
import { getClassMemberName } from "./member-names.js";
import { resolveHeritageReferenceType } from "../../../heritage-reference-type.js";
/**
 * Convert a single class member
 */
const convertClassMember = (node, ctx, superClass, constructorParams) => {
    if (ts.isPropertyDeclaration(node)) {
        return convertProperty(node, ctx, superClass);
    }
    if (ts.isConstructorDeclaration(node)) {
        return convertConstructor(node, ctx, constructorParams);
    }
    return null;
};
/**
 * Filter members to only include those declared directly on this class.
 * DETERMINISTIC: Uses AST structure only, all members in node.members are own members.
 */
const filterOwnMembers = (node) => {
    // All members directly on node.members ARE own members by definition
    // The AST doesn't include inherited members in the class's members array
    //
    // NOTE: `declare` members are type-only in TypeScript and must not emit.
    return node.members.filter((m) => !hasDeclareModifier(m));
};
/**
 * Deduplicate members by name, keeping first occurrence
 */
const deduplicateMembers = (members) => {
    const seenNames = new Set();
    return members.filter((member) => {
        if (member.kind === "constructorDeclaration") {
            return true; // Always include constructor
        }
        if (member.kind === "methodDeclaration") {
            return true; // Never deduplicate methods; overload sets are allowed.
        }
        const name = member.kind === "propertyDeclaration" ? member.name : null;
        if (!name)
            return true;
        if (seenNames.has(name)) {
            return false; // Skip duplicate
        }
        seenNames.add(name);
        return true;
    });
};
/**
 * Check if a type reference is the struct marker.
 * DETERMINISTIC: Uses only the AST expression text, not TypeScript type resolution.
 */
const isStructMarker = (typeRef) => {
    // Check the expression directly - it should be an identifier named "struct" or "Struct"
    if (ts.isIdentifier(typeRef.expression)) {
        const name = typeRef.expression.text;
        return name === "struct" || name === "Struct";
    }
    return false;
};
/**
 * Unwrap `Interface<T>` in heritage clauses.
 *
 * TypeScript nominal interface brands add internal `__tsonic_iface_*` members that
 * make `implements IFoo` noisy for user-authored classes. We provide
 * `Interface<IFoo>` (from @tsonic/core/lang) to strip those members at the TS layer.
 *
 * For IR + C# emission, we want the underlying CLR interface `IFoo`.
 */
const unwrapInterfaceHeritageType = (typeRef) => {
    if (ts.isIdentifier(typeRef.expression) &&
        typeRef.expression.text === "Interface" &&
        typeRef.typeArguments?.length === 1) {
        const only = typeRef.typeArguments[0];
        if (only)
            return only;
    }
    // ExpressionWithTypeArguments is a TypeNode in TS's AST for heritage clauses.
    return typeRef;
};
/**
 * Convert class declaration to IR
 */
export const convertClassDeclaration = (node, ctx) => {
    if (!node.name)
        return null;
    const superClass = node.heritageClauses?.find((h) => h.token === ts.SyntaxKind.ExtendsKeyword)?.types[0];
    // Detect struct marker in implements clause
    let isStruct = false;
    const implementsClause = node.heritageClauses?.find((h) => h.token === ts.SyntaxKind.ImplementsKeyword);
    const implementsTypes = implementsClause?.types
        .filter((t) => {
        if (isStructMarker(t)) {
            isStruct = true;
            return false; // Remove marker from implements
        }
        return true;
    })
        .map((t) => ctx.typeSystem.typeFromSyntax(ctx.binding.captureTypeSyntax(unwrapInterfaceHeritageType(t)))) ?? [];
    // Filter to only include members declared directly on this class (not inherited)
    const ownMembers = filterOwnMembers(node);
    const constructorImplementation = ownMembers.find(ts.isConstructorDeclaration);
    // Extract parameter properties from the implementation constructor when present.
    const parameterProperties = extractParameterProperties(constructorImplementation, ctx);
    const accessorPairs = new Map();
    for (const member of ownMembers) {
        if (ts.isGetAccessorDeclaration(member) ||
            ts.isSetAccessorDeclaration(member)) {
            const name = getClassMemberName(member.name);
            const entry = accessorPairs.get(name) ?? {};
            if (ts.isGetAccessorDeclaration(member)) {
                entry.getter = member;
            }
            else {
                entry.setter = member;
            }
            accessorPairs.set(name, entry);
        }
    }
    const convertedMembers = [];
    const seenAccessors = new Set();
    for (const member of ownMembers) {
        if (ts.isGetAccessorDeclaration(member) ||
            ts.isSetAccessorDeclaration(member)) {
            const name = getClassMemberName(member.name);
            if (seenAccessors.has(name))
                continue;
            seenAccessors.add(name);
            const pair = accessorPairs.get(name);
            convertedMembers.push(convertAccessorProperty(name, pair?.getter, pair?.setter, ctx, superClass));
            continue;
        }
        if (ts.isMethodDeclaration(member)) {
            convertedMembers.push(convertMethod(member, ctx, superClass));
            continue;
        }
        if (ts.isConstructorDeclaration(member)) {
            convertedMembers.push(convertConstructor(member, ctx, constructorImplementation?.parameters));
            continue;
        }
        const converted = convertClassMember(member, ctx, superClass, constructorImplementation?.parameters);
        if (converted) {
            convertedMembers.push(converted);
        }
    }
    // Deduplicate members by name (keep first occurrence)
    // Parameter properties should take precedence over regular properties with same name
    const allMembers = [...parameterProperties, ...convertedMembers];
    const deduplicatedMembers = deduplicateMembers(allMembers);
    // Filter out __brand property if this is a struct
    const finalMembers = isStruct
        ? deduplicatedMembers.filter((m) => m.kind !== "propertyDeclaration" || m.name !== "__brand")
        : deduplicatedMembers;
    return {
        kind: "classDeclaration",
        name: node.name.text,
        typeParameters: convertTypeParameters(node.typeParameters, ctx),
        superClass: superClass
            ? resolveHeritageReferenceType(superClass, ctx)
            : undefined,
        implements: implementsTypes,
        members: finalMembers,
        isExported: hasExportModifier(node),
        isStruct,
    };
};
//# sourceMappingURL=orchestrator.js.map