import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/**
 * Copyright (c) 2020-present, Goldman Sachs
 *
 * 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.
 */
import { Handle, Position } from '@xyflow/react';
import { EyeIcon, KeyIcon, PURE_DatabaseIcon, PURE_DatabaseTableIcon, clsx, } from '@finos/legend-art';
import { observer } from 'mobx-react-lite';
import { getColumnTypeLabel, getTableColumns, isPrimaryKey, resolveViewColumnFormula, resolveViewGroupByFormula, summarizeMilestoning, } from './DatabaseDiagramHelper.js';
import { DatabaseAnnotationDisplay } from './DatabaseAnnotationDisplay.js';
/**
 * View-only React Flow node representing a single relation (Table or View).
 *
 * Layout: header (icon + relation name + schema badge + optional VIEW tag) +
 * a list of column rows. Each row is a fixed-height grid:
 *   - Tables: [PK key icon | column name | column type]
 *   - Views:  [bullet       | column name | formula placeholder]
 *
 * Two invisible Handles (left/right) let React Flow route edges into either
 * side of the box without committing to a specific column anchor.
 */
export const DatabaseTableNode = observer((props) => {
    const { relation, kind, schemaName, isSelected, isJoinEndpoint, fkColumns, selectedColumn, selectedViewColumnName, viewColumnFormulas, viewGroupByFormulas, } = props.data;
    const isViewKind = kind === 'view';
    return (_jsxs("div", { className: clsx('database-diagram__table-node', {
            'database-diagram__table-node--selected': isSelected,
            'database-diagram__table-node--join-endpoint': isJoinEndpoint,
            'database-diagram__table-node--view': isViewKind,
        }), children: [_jsx(Handle, { type: "target", position: Position.Left, className: "database-diagram__table-node__handle", isConnectable: false }), _jsx(Handle, { type: "source", position: Position.Right, className: "database-diagram__table-node__handle", isConnectable: false }), _jsxs("div", { className: "database-diagram__table-node__header", children: [_jsx("div", { className: "database-diagram__table-node__header__icon", children: isViewKind ? _jsx(EyeIcon, {}) : _jsx(PURE_DatabaseTableIcon, {}) }), _jsx("div", { className: "database-diagram__table-node__header__name", children: relation.name }), isViewKind && (_jsx("div", { className: "database-diagram__table-node__header__kind-tag", children: "VIEW" })), isViewKind && renderViewMetadataTags(relation), !isViewKind && renderMilestoningTags(relation), _jsx("div", { className: "database-diagram__table-node__header__schema", children: schemaName }), _jsx(DatabaseAnnotationDisplay, { stereotypes: relation.stereotypes, taggedValues: relation.taggedValues, layout: "compact" })] }), _jsx("div", { className: "database-diagram__table-node__columns", children: isViewKind
                    ? renderViewColumns(relation, viewColumnFormulas, selectedViewColumnName)
                    : renderTableColumns(relation, fkColumns, selectedColumn) }), isViewKind &&
                renderViewGroupBySection(relation, viewGroupByFormulas)] }));
});
// Function declarations (not const arrow) so they're hoisted and the React
// component above can call them without triggering `no-use-before-define`.
function renderTableColumns(table, fkColumns, selectedColumn) {
    return getTableColumns(table).map((column) => {
        const isPk = isPrimaryKey(table, column.name);
        const isFk = fkColumns.has(column);
        const isFocused = selectedColumn === column;
        return (_jsxs("div", { className: clsx('database-diagram__table-node__column', {
                'database-diagram__table-node__column--pk': isPk,
                'database-diagram__table-node__column--fk': isFk,
                'database-diagram__table-node__column--nullable': column.nullable === true,
                'database-diagram__table-node__column--focused': isFocused,
            }), title: column.nullable ? `${column.name} (nullable)` : column.name, children: [_jsx("div", { className: "database-diagram__table-node__column__key", children: isPk ? _jsx(KeyIcon, {}) : null }), _jsx("div", { className: "database-diagram__table-node__column__name", children: column.name }), _jsx("div", { className: "database-diagram__table-node__column__type", children: getColumnTypeLabel(column) })] }, column.name));
    });
}
/**
 * View columns are defined by `view.columnMappings` rather than `Column[]`.
 * Each mapping carries the column name and a relational operation that
 * computes the value. We render the Pure code returned by the engine (cached
 * in `DatabaseEditorState.viewColumnFormulas`); while it's still loading we
 * fall back to a placeholder so the layout stays stable.
 *
 * The `title` attribute also surfaces the formula for tooltip-on-hover, since
 * complex formulas may not fit in the 1-line display.
 */
function renderViewColumns(view, formulas, selectedViewColumnName) {
    return view.columnMappings.map((mapping) => {
        const isPk = isPrimaryKey(view, mapping.columnName);
        const isFocused = selectedViewColumnName === mapping.columnName;
        const formula = resolveViewColumnFormula(formulas, view.schema.name, view.name, mapping.columnName);
        return (_jsxs("div", { className: clsx('database-diagram__table-node__column', {
                'database-diagram__table-node__column--pk': isPk,
                'database-diagram__table-node__column--view': true,
                'database-diagram__table-node__column--focused': isFocused,
            }), title: `${mapping.columnName}: ${formula}`, children: [_jsx("div", { className: "database-diagram__table-node__column__key", children: isPk ? _jsx(KeyIcon, {}) : null }), _jsx("div", { className: "database-diagram__table-node__column__name", children: mapping.columnName }), _jsx("div", { className: "database-diagram__table-node__column__type", children: formula })] }, mapping.columnName));
    });
}
/**
 * Render the secondary view-metadata tags shown next to the primary "VIEW"
 * tag in the canvas node header. These mirror the tags rendered in the
 * schema tree's view row (see `DatabaseTreeViewRow`) so the two surfaces
 * stay in sync.
 *
 * Returns `null` when the view has none of these features set, so the
 * header stays compact for "plain" views.
 */
function renderViewMetadataTags(view) {
    const groupByCount = view.groupBy?.columns.length ?? 0;
    const hasDistinct = view.distinct === true;
    const hasFilter = Boolean(view.filter);
    const hasGroupBy = groupByCount > 0;
    if (!hasDistinct && !hasFilter && !hasGroupBy) {
        return null;
    }
    return (_jsxs(_Fragment, { children: [hasDistinct && (_jsx("div", { className: "database-diagram__table-node__header__kind-tag database-diagram__table-node__header__kind-tag--distinct", title: "View applies DISTINCT", children: "DISTINCT" })), hasFilter && view.filter && (_jsx("div", { className: "database-diagram__table-node__header__kind-tag database-diagram__table-node__header__kind-tag--filtered", title: `Filtered by ${view.filter.filter.ownerReference.valueForSerialization ?? ''}.${view.filter.filterName}`, children: "FILTERED" })), hasGroupBy && (_jsx("div", { className: "database-diagram__table-node__header__kind-tag database-diagram__table-node__header__kind-tag--grouped", title: `GROUP BY ${groupByCount} expression${groupByCount === 1 ? '' : 's'}`, children: `GROUP BY (${groupByCount})` }))] }));
}
/**
 * Compact header tags surfacing a table's `milestoning` configuration.
 * One tag per Milestoning entry (a table can declare both business and
 * processing milestoning). The tag color follows the kind classifier from
 * `summarizeMilestoning` so business and processing read distinctly.
 *
 * Returns `null` for non-milestoned tables to keep the header compact \u2014
 * the vast majority of tables are not milestoned.
 */
function renderMilestoningTags(table) {
    if (table.milestoning.length === 0) {
        return null;
    }
    return (_jsx(_Fragment, { children: table.milestoning.map((milestoning) => {
            const summary = summarizeMilestoning(milestoning);
            return (_jsx("div", { className: clsx('database-diagram__table-node__header__kind-tag', `database-diagram__table-node__header__kind-tag--milestoned-${summary.kind}`), title: summary.description, children: summary.label }, summary.label));
        }) }));
}
/**
 * Footer rendered under the column rows when a view declares `groupBy`.
 * Lists each grouping expression as one row of Pure code (lazy-resolved
 * from `viewGroupByFormulas` — falls back to a placeholder while the
 * batched engine call is in flight).
 *
 * Returns `null` for views with no groupBy and for table-kind nodes, so the
 * footer doesn't add visual weight to nodes that don't need it.
 */
function renderViewGroupBySection(view, formulas) {
    const columns = view.groupBy?.columns ?? [];
    if (columns.length === 0) {
        return null;
    }
    return (_jsxs("div", { className: "database-diagram__table-node__group-by", title: `GROUP BY (${columns.length})`, children: [_jsx("div", { className: "database-diagram__table-node__group-by__header", children: "GROUP BY" }), columns.map((_, index) => {
                const formula = resolveViewGroupByFormula(formulas, view.schema.name, view.name, index);
                return (_jsxs("div", { className: "database-diagram__table-node__group-by__row", title: formula, children: [_jsx("span", { className: "database-diagram__table-node__group-by__index", children: `[${index}]` }), _jsx("span", { className: "database-diagram__table-node__group-by__formula", children: formula })] }, formula));
            })] }));
}
/**
 * Compact stub rendered when a join references a relation in another
 * database. Shows the schema/name plus the owning database path, with a
 * dashed border to distinguish it from real in-database table nodes. Two
 * invisible Handles let React Flow attach edges to either side. Not
 * selectable — the canvas swallows clicks on stubs.
 */
export const DatabaseForeignRelationStubNode = observer((props) => {
    const { schemaName, relationName, ownerPath, isJoinEndpoint } = props.data;
    return (_jsxs("div", { className: clsx('database-diagram__table-node', 'database-diagram__table-node--foreign-stub', {
            'database-diagram__table-node--join-endpoint': isJoinEndpoint,
        }), title: `${schemaName}.${relationName}\n(in database: ${ownerPath})`, children: [_jsx(Handle, { type: "target", position: Position.Left, className: "database-diagram__table-node__handle", isConnectable: false }), _jsx(Handle, { type: "source", position: Position.Right, className: "database-diagram__table-node__handle", isConnectable: false }), _jsxs("div", { className: "database-diagram__table-node__header", children: [_jsx("div", { className: "database-diagram__table-node__header__icon", children: _jsx(PURE_DatabaseIcon, {}) }), _jsx("div", { className: "database-diagram__table-node__header__name", children: `${schemaName}.${relationName}` })] }), _jsx("div", { className: "database-diagram__table-node__foreign-stub__owner", children: ownerPath })] }));
});
//# sourceMappingURL=DatabaseTableNode.js.map