<template>
    <div class="ui-dynamic-table-container">
        <UIDynamicTableTitleText
            v-if="props.title_text && props.title_text.length > 0"
            :title="props.title_text"
            :style="props.title_style || 'default'"
            :customStyles="props.title_custom_text_styling || ''"
            :titleIcon="props.title_icon || ''"
        />

        <!-- Search and Reload Controls -->
        <v-toolbar v-show="props.showToolbar" flat class="pb-4 pt-8">
            <v-text-field
                class="search-input"
                :class="{ 'mobile-search': isMobile }"
                v-model="search"
                label="Suchen"
                prepend-inner-icon="mdi-magnify"
                variant="outlined"
                hide-details
                single-line
            >
            </v-text-field>
            <v-btn
                class="reload-button ml-4"
                @click="reloadData"
                :disabled="isReloading"
                size="small"
                variant="outlined"
            >
                <v-icon left :class="{ 'reload-spin': isReloading }">mdi-refresh</v-icon>
            </v-btn>
        </v-toolbar>

        <!-- Desktop Table View -->
        <v-data-table
            v-show="!isMobile"
            :headers="headers"
            :items="filteredTasks"
            :sort-by="sortBy"
            :items-per-page="itemsPerPage"
            :items-per-page-options="itemsPerPageOptions"
            @update:sort-by="updateSortBy"
            @update:items-per-page="updateItemsPerPage"
            class="full-width-table"
        >
            <template v-slot:item.actions="{ item }">
                <v-container class="action-button-container">
                    <v-container
                        v-for="(action, index) in actions"
                        style="padding: 0px; display: inline; width: fit-content; margin: 0px"
                        :key="index"
                    >
                        <v-btn
                            style="margin: 0px 2px"
                            class="action-button"
                            v-if="action && conditionCheck(action.condition, item)"
                            size="small"
                            @click="actionFn(action, item)"
                        >
                            {{ getActionLabel(action, item) }}
                        </v-btn>
                    </v-container>
                </v-container>
            </template>
            <template v-slot:no-data>
                <v-alert text="Keine Daten" style="margin: 12px"></v-alert>
            </template>
        </v-data-table>

        <!-- Mobile Cards View -->
        <div v-show="isMobile" class="mobile-cards-container">
            <v-card
                v-for="(item, index) in paginatedTasksForMobile"
                :key="index"
                class="mobile-card ma-2"
                variant="outlined"
                elevation="2"
            >
                <v-card-text class="pb-2">
                    <div v-for="header in dataHeaders" :key="header.key" class="mobile-card-row">
                        <span class="mobile-card-label">{{ header.title }}:</span>
                        <span class="mobile-card-value">{{ getNestedProperty(item, header.key) }}</span>
                    </div>
                </v-card-text>
                <v-card-actions v-if="actions && actions.length > 0" class="pt-0 px-2 pb-2 d-flex">
                    <v-btn
                        v-for="(action, actionIndex) in visibleActionsForMobile(item)"
                        :key="actionIndex"
                        size="x-small"
                        color="primary"
                        variant="flat"
                        class="flex-grow-1 mx-1"
                        style="min-height: 28px; height: 28px; font-size: 0.75rem"
                        @click="actionFn(action, item)"
                    >
                        {{ getActionLabel(action, item) }}
                    </v-btn>
                </v-card-actions>
            </v-card>

            <!-- No Data Alert for Mobile -->
            <v-alert v-if="paginatedTasksForMobile.length === 0" text="Keine Daten" class="ma-2"></v-alert>
        </div>

        <!-- Pagination for Mobile -->
        <v-pagination
            v-if="isMobile && filteredTasks.length > itemsPerPage"
            v-model="currentPage"
            :length="Math.ceil(filteredTasks.length / itemsPerPage)"
            class="mt-4"
        ></v-pagination>
    </div>
</template>

<script>
import { mapState } from 'vuex';
import UIDynamicTableTitleText from './TitleText.vue';
import { debounce } from 'lodash';
import jsonata from 'jsonata';

export default {
    name: 'UIDynamicTable',
    components: {
        UIDynamicTableTitleText,
    },
    inject: ['$socket'],
    props: {
        id: { type: String, required: true },
        props: { type: Object, default: () => ({}) },
        state: {
            type: Object,
            default: () => ({ enabled: true, visible: true }),
        },
    },
    computed: {
        ...mapState('data', ['messages']),
        searchParamKey() {
            return `search_${this.id}`;
        },
        sortByStorageKey() {
            return `table_sort_${this.id}`;
        },
        itemsPerPageStorageKey() {
            return `table_items_per_page_${this.id}`;
        },
        isMobile() {
            return this.$vuetify.display.mobile;
        },
        dataHeaders() {
            return this.headers.filter((header) => header.key !== 'actions');
        },
        filteredTasks() {
            let filtered = this.tasks;

            // Apply search filter
            if (this.search && this.search.trim() !== '') {
                const searchTerm = this.search.toLowerCase();
                filtered = filtered.filter((item) => {
                    return this.dataHeaders.some((header) => {
                        const value = item[header.key];
                        return value && value.toString().toLowerCase().includes(searchTerm);
                    });
                });
            }

            // Don't apply pagination here - let v-data-table handle it for desktop
            // For mobile, we'll handle pagination separately
            return filtered;
        },
        paginatedTasksForMobile() {
            if (!this.isMobile) return this.filteredTasks;

            const startIndex = (this.currentPage - 1) * this.itemsPerPage;
            const endIndex = startIndex + this.itemsPerPage;
            return this.filteredTasks.slice(startIndex, endIndex);
        },
        // Computed property to ensure reactivity to message changes
        currentMessage() {
            return this.messages[this.id] || {};
        },
    },
    data() {
        return {
            search: '',
            actions: [],
            tasks: [],
            headers: [],
            isReloading: false,
            isInitialized: false,
            sortBy: [],
            itemsPerPage: 10,
            currentPage: 1,
            itemsPerPageOptions: [
                { value: 5, title: '5' },
                { value: 10, title: '10' },
                { value: 25, title: '25' },
                { value: 50, title: '50' },
                { value: 100, title: '100' },
                { value: -1, title: 'All' },
            ],
            // Store JSONata evaluation results for each action-row combination
            jsonataResults: new Map(),
        };
    },
    mounted() {
        this.loadFromLocalStorage();
        this.loadSearchFromUrl();
        this.isInitialized = true;

        this.$socket.on('widget-load:' + this.id, (msg) => {
            this.initialize(msg);
            this.$store.commit('data/bind', {
                widgetId: this.id,
                msg,
            });
        });
        this.$socket.on('msg-input:' + this.id, (msg) => {
            if (Array.isArray(msg)) {
                msg = msg.filter((obj) => obj._client.socketId == this.$socket.id)[0];
            }

            this.initialize(msg);
            this.messages[this.id] = msg;
            this.$store.commit('data/bind', {
                widgetId: this.id,
                msg,
            });
        });
        this.$socket.emit('widget-load', this.id);
    },
    unmounted() {
        this.$socket?.off('widget-load' + this.id);
        this.$socket?.off('msg-input:' + this.id);
    },
    methods: {
        send(msg, index) {
            const msgArr = [];
            msgArr[index] = msg;
            this.$socket.emit('widget-action', this.id, msgArr);
        },
        visibleActionsForMobile(row) {
            return this.actions.filter((action) => {
                if (!action) return false;

                // If no condition or empty condition, show the action
                if (!action.condition || action.condition === '') {
                    return true;
                } else {
                    return this.conditionCheck(action.condition, row);
                }
                // For conditions, we'll show all actions and let the click handler deal with it
                // This avoids the Vue reactivity issue with calling conditionCheck in template
                //return true;
            });
        },
        actionFn(action, item) {
            // Check condition at click time if it exists
            if (action.condition && action.condition !== '') {
                if (!this.conditionCheck(action.condition, item)) {
                    console.log('Action condition not met:', action.condition);
                    return; // Don't execute if condition fails
                }
            }

            const msg = this.messages[this.id] || {};
            msg.payload = item;
            this.send(
                msg,
                this.actions.findIndex((element) => element.label === action.label)
            );
        },
        initialize(msg) {
            // Handle null or undefined messages
            if (!msg) {
                this.tasks = [];
                this.actions = (this.props.options || []).filter((action) => action != null);
                this.headers = (this.props.columns || []).map((item) => ({
                    title: item.label,
                    key: item.value,
                }));
                this.headers.push({
                    title: '',
                    align: 'center',
                    key: 'actions',
                    sortable: false,
                });
                // Clear JSONata results cache
                this.jsonataResults.clear();
                return;
            }

            this.tasks = msg.payload || [];

            // Always use props.options for frontend-only evaluation
            this.actions = (this.props.options || []).filter((action) => action != null);

            this.headers = (this.props.columns || []).map((item) => ({
                title: item.label,
                key: item.value,
            }));

            this.headers.push({
                title: '',
                align: 'center',
                key: 'actions',
                sortable: false,
            });

            // Clear JSONata results cache when new data arrives
            this.jsonataResults.clear();
        },
        getActionLabel(action, item) {
            // Handle typed input system
            if (action.label_type) {
                switch (action.label_type) {
                    case 'str':
                        return action.label || '';

                    case 'msg':
                        if (action.label) {
                            const msgData = this.messages[this.id] || {};
                            return this.getNestedProperty(msgData, action.label) || '';
                        }
                        return '';

                    case 'row':
                        if (action.label) {
                            return this.getNestedProperty(item, action.label) || '';
                        }
                        return '';

                    case 'jsonata':
                        // For JSONata, check if we have the expression from backend
                        const msgData = this.messages[this.id] || {};
                        const actionIndex = this.actions.indexOf(action);

                        if (msgData._jsonataLabels && msgData._jsonataLabels[actionIndex]) {
                            const result = this.evaluateJsonata(msgData._jsonataLabels[actionIndex], msgData, item);
                            return result || '';
                        }
                        return action.label || '';

                    default:
                        return action.label || '';
                }
            }

            // Fallback to legacy handling for backward compatibility
            const legacy = this.evaluateLegacyLabel(action, item);
            return legacy || action.label || '';
        },

        getNestedProperty(obj, path) {
            if (!path) return '';

            const keys = path.split('.');
            let current = obj;

            for (const key of keys) {
                if (current && typeof current === 'object' && key in current) {
                    current = current[key];
                } else {
                    return path; // Return original path as fallback
                }
            }

            return current !== undefined && current !== null ? String(current) : path;
        },

        evaluateJsonata(expression, msgData, rowData) {
            try {
                // Create a unique key for this evaluation using expression and a hash of row data
                const rowHash = JSON.stringify(rowData).slice(0, 50); // Truncate for performance
                const key = `${expression}_${rowHash}`;

                // Check if we already have a result for this combination
                if (this.jsonataResults.has(key)) {
                    return this.jsonataResults.get(key);
                }

                // Create a combined context with both message and row data
                const context = {
                    ...msgData,
                    $: rowData,
                    payload: msgData.payload,
                    msg: msgData,
                };

                const expr = jsonata(expression);
                const result = expr.evaluate(context);

                // Handle Promise return from JSONata
                if (result && typeof result.then === 'function') {
                    // Store a pending indicator first
                    this.jsonataResults.set(key, expression);

                    // Handle the Promise
                    result
                        .then((resolvedValue) => {
                            const finalValue =
                                resolvedValue !== undefined && resolvedValue !== null ? String(resolvedValue) : '';
                            this.jsonataResults.set(key, finalValue);
                            this.$forceUpdate();
                        })
                        .catch((error) => {
                            console.warn('JSONata Promise evaluation error:', error.message);
                            this.jsonataResults.set(key, expression);
                            this.$forceUpdate();
                        });

                    return expression; // Return expression as placeholder
                }

                // Synchronous result
                const finalValue = result !== undefined && result !== null ? String(result) : '';
                this.jsonataResults.set(key, finalValue);
                return finalValue;
            } catch (error) {
                console.warn('JSONata evaluation error:', error.message);
                return expression; // fallback to original expression
            }
        },

        evaluateLegacyLabel(action, item) {
            // Legacy support for old "msg." and "row." prefixes
            if (action.label && (action.label.startsWith('msg.') || action.label.startsWith('row.'))) {
                let sourceObject;
                let path;

                if (action.label.startsWith('msg.')) {
                    sourceObject = this.messages[this.id] || {};
                    path = action.label.substring(4).split('.');
                } else if (action.label.startsWith('row.')) {
                    sourceObject = item;
                    path = action.label.substring(4).split('.');
                }

                // Navigate through the object using the path
                let value = sourceObject;
                for (const key of path) {
                    if (value && typeof value === 'object' && key in value) {
                        value = value[key];
                    } else {
                        // Path not found, return original label as fallback
                        return action.label;
                    }
                }

                return value !== undefined && value !== null ? String(value) : action.label;
            }

            return action.label || '';
        },
        conditionCheck(condition, row) {
            if (!condition || condition === '') return true;
            try {
                const func = new Function('row', `return ${condition}`);
                return func(row);
            } catch (error) {
                console.error('Error evaluating condition:', error);
                return false;
            }
        },
        reloadData() {
            this.isReloading = true;

            this.send(
                {
                    _client: {
                        socketId: this.$socket.id,
                        widgetId: this.id,
                    },
                    payload: {
                        action: 'reload',
                    },
                },
                0
            );

            setTimeout(() => {
                this.isReloading = false;
            }, 1000);
        },
        updateSearchParam: debounce(function (searchValue) {
            const currentQuery = { ...this.$route.query };

            if (searchValue && searchValue.trim() !== '') {
                currentQuery[this.searchParamKey] = searchValue;
            } else {
                delete currentQuery[this.searchParamKey];
            }

            this.$router
                .replace({
                    path: this.$route.path,
                    query: currentQuery,
                })
                .catch((err) => {
                    if (err.name !== 'NavigationDuplicated') {
                        console.error('Router navigation error:', err);
                    }
                });
        }, 500),
        loadSearchFromUrl() {
            const searchValue = this.$route.query[this.searchParamKey];
            if (searchValue && typeof searchValue === 'string') {
                this.search = searchValue;
            } else {
                this.search = '';
            }
        },
        loadFromLocalStorage() {
            try {
                const savedSortBy = localStorage.getItem(this.sortByStorageKey);
                if (savedSortBy) {
                    this.sortBy = JSON.parse(savedSortBy);
                }

                const savedItemsPerPage = localStorage.getItem(this.itemsPerPageStorageKey);
                if (savedItemsPerPage) {
                    this.itemsPerPage = parseInt(savedItemsPerPage, 10);
                }
            } catch (error) {
                console.error('Error loading table settings from localStorage:', error);
            }
        },
        saveToLocalStorage() {
            try {
                localStorage.setItem(this.sortByStorageKey, JSON.stringify(this.sortBy));
                localStorage.setItem(this.itemsPerPageStorageKey, this.itemsPerPage.toString());
            } catch (error) {
                console.error('Error saving table settings to localStorage:', error);
            }
        },
        updateSortBy(newSortBy) {
            this.sortBy = newSortBy;
            this.saveToLocalStorage();
        },
        updateItemsPerPage(newItemsPerPage) {
            this.itemsPerPage = newItemsPerPage;
            this.saveToLocalStorage();
        },
    },
    watch: {
        search(newValue, oldValue) {
            if (this.isInitialized) {
                this.updateSearchParam(newValue);
            }
        },
        '$route.query': {
            handler(newQuery) {
                if (this.isInitialized) {
                    this.loadSearchFromUrl();
                }
            },
            deep: true,
        },
    },
};
</script>

<style scoped>
@import '../stylesheets/ui-dynamic-table.css';

/* Search bar responsive styles */
.search-input {
    margin-left: 12px;
    margin-right: 50%;
}

.mobile-search {
    margin-right: 12px !important;
    flex: 1;
}

/* Mobile Cards Styles */
.mobile-cards-container {
    padding: 8px;
}

.mobile-card {
    margin-bottom: 12px !important;
    border-radius: 8px;
}

.mobile-card-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 4px 0;
    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}

.mobile-card-row:last-child {
    border-bottom: none;
}

.mobile-card-label {
    font-weight: 600;
    color: rgba(0, 0, 0, 0.7);
    flex: 0 0 40%;
    font-size: 0.875rem;
}

.mobile-card-value {
    flex: 1;
    text-align: right;
    word-break: break-word;
    font-size: 0.875rem;
}

/* Responsive adjustments */
@media (max-width: 600px) {
    .mobile-cards-container {
        padding: 4px;
    }

    .mobile-card {
        margin-bottom: 8px !important;
    }

    .mobile-card-label {
        flex: 0 0 45%;
        font-size: 0.8rem;
    }

    .mobile-card-value {
        font-size: 0.8rem;
    }

    .search-input {
        margin-left: 8px;
        margin-right: 8px;
    }
}
</style>
