// Silence may be golden, but sometimes words paint the canvas of understanding.
const DatabaseReplace = {
	name: 'DatabaseReplace',
	directives: {
		'linksy-tooltip': LinksyTooltip,
	},
	components: {
		LinksyButton,
		LinksyDropdown,
		LinksyDataTable,
		LinksyPagination,
		PagesColumn,
		DatabaseUserControls,
		LinksyDatabaseFilters
	},
	data() {
		return {
			search: '',
			prevQuery: '',
			filter: {
                column: null,
                table: null,
                search_mode: ['contains'],
                query: "",
				notQuery: "",
				ignoreImgUrls: true,
				caseSensitive: false,
				fullMatch: false,
				ignoreGuids: true,
            },
			columns: [
				{
					name: 'Tables',
				},
				{
					name: 'Column/Result',
				},
			],
			isGettingOffset: false,
			isLoading: false,
			databaseReplacement: '',
			databaseList: [],
			selectedTables: [],
			selectedItems: [],
			selectedSearchType: 'CONTAINS',
			selectedAction: null,
			filterSearch: '',
			replacer: new DatabaseReplacer(LINKSY_SECURE_TOKEN),
			isDatabaseReplacing: false,
			activeItem: null,
			selectedItems: [],
			showFilter: false,
			isApplying: false,
			startsWith: '',
			endsWith: ''
		}
	},
	computed: {
		isContains: {
            get() {
                return !(this.filter.search_mode.includes("not-contains") && !this.filter.search_mode.includes("contains"));
            }
        },
		isApplyDisabled: {
			get() {
				return this.selectedAction === null || this.isApplying || this.selectedItems.length < 1
			}
		},
		pageLength: {
			get() {
				return Object.keys(this.items).length
			},
		},
		items: {
			get() {
				if (!this.replacer?.data) return [];
		
				return this.replacer.data
					.map(table => {
						if (this.filter.table && table.table_name !== this.filter.table) {
							return null;
						}
		
						let filteredColumns = table.columns_results
							.filter(column =>
								(!this.filter.column || column.column_name === this.filter.column) &&
								column.results.some(res => {
									if (this.filter.query || this.filter.notQuery) {
										const caseSensitive = this.filter.caseSensitive;
										const query = caseSensitive ? this.filter.query : this.filter.query.toLowerCase();
										const notQuery = caseSensitive ? this.filter.notQuery : this.filter.notQuery.toLowerCase();
										const resValue = caseSensitive ? res.value : res.value.toLowerCase();

										let isMatch = true;
		
										if (this.filter.search_mode.includes('contains') && this.filter.search_mode.includes('not-contains')) {
											if (!resValue.includes(query) || resValue.includes(notQuery)) {
												isMatch = false;
											}
										} else {
											if (this.filter.search_mode.includes('contains') && !resValue.includes(query)) {
												isMatch = false;
											}
											if (this.filter.search_mode.includes('not-contains') && resValue.includes(notQuery)) {
												isMatch = false;
											}
										}
		
										return isMatch;
									} else {
										return true;
									}
								})
							)
							.map(column => ({
								...column,
								results: column.results.filter(res => {
									if (this.filter.query || this.filter.notQuery) {
										const caseSensitive = this.filter.caseSensitive;
										const query = caseSensitive ? this.filter.query : this.filter.query.toLowerCase();
										const notQuery = caseSensitive ? this.filter.notQuery : this.filter.notQuery.toLowerCase();
										const resValue = caseSensitive ? res.value : res.value.toLowerCase();

										let isMatch = true;
		
										if (this.filter.search_mode.includes('contains') && this.filter.search_mode.includes('not-contains')) {
											if (!resValue.includes(query) || resValue.includes(notQuery)) {
												isMatch = false;
											}
										} else {
											if (this.filter.search_mode.includes('contains') && !resValue.includes(query)) {
												isMatch = false;
											}
											if (this.filter.search_mode.includes('not-contains') && resValue.includes(notQuery)) {
												isMatch = false;
											}
										}
		
										return isMatch;
									} else {
										return true;
									}
								})
							}));
		
						return filteredColumns.length ? { ...table, columns_results: filteredColumns } : null;
					})
					.filter(table => table !== null);
			}
		},				
		pagination: {
			get() {
				return this.replacer.pagination
			},
		},
		actions() {
			return DatabaseReplacer.SUPPORTED_ACTIONS
		},
		searchTypes: {
			get() {
				return DatabaseReplacer.SEARCH_TYPES
			},
		},
		filterModes: {
			get() {
				return DatabaseReplacer.FILTER_MODES
			},
		},
		isAllRowsSelected() {
			if (this.selectedItems.length === 0) return false;
			return this.items.every(item =>
				this.selectedItems.some(selected =>
					selected.table_name === item.table_name &&
					item.columns_results.every(col =>
						col.results.every(res =>
							selected.columns.some(selCol =>
								selCol.column_name === col.column_name &&
								selCol.items.includes(res)
							)
						)
					)
				)
			);
		},		
		isStartsWithEndsWith: {
            get() {
                return this.selectedSearchType === 'STARTS_ENDS_WITH';
            }
        }
	},
	methods: {
		handleSearch(q) {
			this.activeItem = null;
		
			if (Array.isArray(q)) {
				if (q[0] === '' || q[1] === '') {
					this.replacer.reset();
				}
		
				if (JSON.stringify(this.search) !== JSON.stringify(q)) {
					this.search = q;
				}
			} else {
				if (q === '') {
					this.replacer.reset();
				}
		
				if (this.search !== q) {
					this.search = q;
				}
			}
		},		
		handleChangePage(page) {
			this.replacer.toPage(page - 1)
			this.getResults()
		},
		handleChangeRowsPerPage(limit) {
			if (limit != this.pagination.limit) {
				this.replacer.pagination = { limit }
				this.getResults()
			}
		},
		onSelectSearchType(type) {
			this.selectedSearchType = type

			// reset data and selected items
			this.replacer.reset()
			this.selectedItems = []
		},

		async getResults() {
			if (!this.search) return
			this.selectedItems = []

			this.prevQuery = this.search

			const selectedTables = this.selectedTables.length > 0 ? this.selectedTables : this.databaseList

			try {
				this.isLoading = true

				const searchType = this.searchTypes.find((e) => e.value === this.selectedSearchType)
				await this.replacer.searchDatabase(this.search, searchType.value, this.filterSearch, selectedTables, this.filter)
			} catch (error) {
				this.$toast.error(error.response?.data?.data || error.message)
			} finally {
				this.isLoading = false
			}
		},

		async getOffsetResults(item) {
			if (!this.search) return

			this.prevQuery = this.search

			try {
				this.isGettingOffset = true

				const searchType = this.searchTypes.find((e) => e.value === this.selectedSearchType)
				await this.replacer.searchColumnOffset(this.search, searchType.value, this.filter, item)

			} catch (error) {
				this.$toast.error(error.response?.data?.data || error.message)
			} finally {
				this.isGettingOffset = false
			}
		},

        async getDatabaseList() {
            try {
				const params = toQueryString({
                    action: 'linksy_search_and_replace_fetch_db_list',
                    nonce: LINKSY_SECURE_TOKEN
                })
        
                const {
                    data: { data },
                } = await axios.get(ajaxurl + '?' + params)

                this.databaseList = data
			} catch (error) {
				this.$toast.error(error.response?.data?.data || error.message)
			}
        },
		handleRowToggle(val, item, index) {
			if (val) {
				this.activeItem = index
			}
		},
		totalResults(columnsResults) {
			return columnsResults.reduce((sum, col) => sum + (col.results ? col.results.length : 0), 0);
		},

		selectAction(index) {
			let action = Object.values(this.actions)[index]
			if (action.value === this.selectedAction) {
				this.selectedAction = null
			} else {
				this.selectedAction = action?.value
			}
		},

		selectAllRows() {
			if (this.items.length === 0) return;
			
			if (this.isAllRowsSelected) {
				this.selectedItems = [];
			} else {
				this.selectedItems = this.items.map(item => ({
					table_name: item.table_name,
					columns: item.columns_results.map(col => ({
						column_name: col.column_name,
						items: [...col.results]
					}))
				}));
			}
		},
		
		isItemSelected(item, resColumn, result, isTableBased) {
			if (isTableBased) {
				const table = this.selectedItems.find(selected => selected.table_name === item.table_name);
				if (!table) return false;
				return item.columns_results.every(col =>
					col.results.every(res =>
						table.columns.some(selectedCol =>
							selectedCol.column_name === col.column_name &&
							selectedCol.items.includes(res)
						)
					)
				);
			} else {
				return this.selectedItems.some(selected =>
					selected.table_name === item.table_name &&
					selected.columns.some(col =>
						col.column_name === resColumn.column_name &&
						col.items.includes(result)
					)
				);
			}
		},
		
		handleSelect(item, resColumn, result, isTableBased) {
			if (isTableBased) {
				const existingIndex = this.selectedItems.findIndex(selected => selected.table_name === item.table_name);
				if (existingIndex > -1) {
					this.selectedItems.splice(existingIndex, 1);
				} else {
					this.selectedItems.push({
						table_name: item.table_name,
						columns: item.columns_results.map(col => ({
							column_name: col.column_name,
							items: [...col.results]
						}))
					});
				}
			} else {
				const table = this.selectedItems.find(selected => selected.table_name === item.table_name);
				if (table) {
					const column = table.columns.find(col => col.column_name === resColumn.column_name);
					if (column) {
						const itemIndex = column.items.indexOf(result);
						if (itemIndex > -1) {
							column.items.splice(itemIndex, 1);
							if (column.items.length === 0) {
								table.columns = table.columns.filter(col => col !== column);
							}
							if (table.columns.length === 0) {
								this.selectedItems = this.selectedItems.filter(selected => selected !== table);
							}
						} else {
							column.items.push(result);
						}
					} else {
						table.columns.push({
							column_name: resColumn.column_name,
							items: [result]
						});
					}
				} else {
					this.selectedItems.push({
						table_name: item.table_name,
						columns: [{
							column_name: resColumn.column_name,
							items: [result]
						}]
					});
				}
			}
		},
		
		handleSelectAll(item, column) {
			const table = this.selectedItems.find(selected => selected.table_name === item.table_name);
			if (table) {
				const columnIndex = table.columns.findIndex(col => col.column_name === column.column_name);
				if (columnIndex > -1) {
					table.columns.splice(columnIndex, 1);
					if (table.columns.length === 0) {
						this.selectedItems = this.selectedItems.filter(selected => selected !== table);
					}
				} else {
					table.columns.push({
						column_name: column.column_name,
						items: [...column.results]
					});
				}
			} else {
				this.selectedItems.push({
					table_name: item.table_name,
					columns: [{
						column_name: column.column_name,
						items: [...column.results]
					}]
				});
			}
		},

		isReplacementFieldEmpty() {
			if (!this.isStartsWithEndsWith && this.databaseReplacement === '') {
				return true;
			} else if (this.isStartsWithEndsWith && (this.startsWith === '' || this.endsWith === '')) {
				return true;
			}

			return false;
		},

		async handleApplyAction() {
			if (!this.isStartsWithEndsWith && this.search !== this.prevQuery) {
				this.$toast.error('Search parameter has changed')

				return
			}

			if (!this.isStartsWithEndsWith && this.search === this.databaseReplacement) {
				this.$toast.error('Nothing to replace')
				return
			}

			if (this.isReplacementFieldEmpty()) {
				this.$toast.error('Replacement field cannot be empty');
				return;
			}

			this.isApplying = true

			try {
				const stats = await this.replacer.applyAction(
					this.search,
					this.selectedSearchType,
					this.selectedAction,
					this.selectedItems,
					this.isStartsWithEndsWith ? [this.startsWith, this.endsWith] : this.databaseReplacement
				);

				const message = `${stats.replaced.length} of ${stats.replaced.length + stats.failed.length} item(s) have been processed!`;
				this.$toast.success(message);
				
				this.selectedItems = []
				this.getResults()
			} catch (error) {
				this.$toast.error(error.response?.data?.data || error.message)
			} finally {
				this.isApplying = false
			}
		},
		
		toggleFilter() {
            this.showFilter = !this.showFilter
        },
		handleFilterChange(values) {
			this.filter = { ...values }
			this.replacer.reset()

			this.getResults()
		},
		handleCancel(s, r) {
			this.selectedAction = null

			s()
			r()
		},
	},
	template: `
        <div id="database-replace" class="d-flex flex-column gap-4">
            <div class="d-flex flex-column flex-lg-row justify-content-between column-gap-2 column-gap-xl-5 row-gap-3">
				<div class="action-dropdown d-flex justify-content-between">
					<linksy-dropdown
						is-manual
						is-async
						:initial-value="actions.find(e => e.value == selectedAction)?.label"
						:options="actions"
						class="bg-white text-dark border border-1 rounded-3"
					>
						<template #title>
							<span class="text-nowrap">Bulk action</span>
						</template>
						<template #item="{ item, index, selected }">
							<div class="d-flex w-100 gap-1 align-items-center" @click="selectAction(index)">
								<div class="linksy-checkbox" :class="{'checked': selectedAction === item.value}" />
								<label class="cursor-pointer">{{ item.label }}</label>
								<span class="ms-auto text-wrap" v-linksy-tooltip="{ message: actions[index]?.message, flow: 'right' }"></span>
							</div>
						</template>
						<template #footer="props">
							<div class="actions-button d-flex gap-2">
								<linksy-button class="flex-grow-1" :disabled="isApplyDisabled" :loading="isApplying" @click="handleApplyAction">Apply</linksy-button>
								<linksy-button variant="secondary" class=" flex-grow-1" @click="() => { handleCancel(props.onReset, props.onSubmit) }">Cancel</linksy-button>
							</div>
						</template>
					</linksy-dropdown>

					<linksy-button variant="white_outline" class="d-flex d-lg-none filter-btn-toggle text-nowrap" :class="{active: showFilter}" @click="toggleFilter">
						<i class="fa-solid fa-bars-staggered me-2"></i><span>Filter</span>
					</linksy-button>
				</div>	

				<div class="d-flex flex-column flex-lg-row align-items-center gap-2 w-100" :class="{'flex-wrap': isStartsWithEndsWith}">
					<div class="dropdown-search-container d-flex flex-column flex-sm-row flex-fill gap-2">
						<linksy-dropdown
							is-multi
							is-searchable
							is-resetable
							key="db_dropdown"
							placeholder="All tables"
							class="database-dropdown bg-white text-dark border border-1 rounded-3"
							:initial-value="selectedTables"
							:options="databaseList"
							@selected="(e) => { selectedTables = e }"
						>
							<template #title>
								<span class="text-nowrap">{{selectedTables.length < 1 ? 'Table' : selectedTables.length + ' Added'}}</span>
							</template>
						</linksy-dropdown>

						<database-user-controls
							:is-loading="isLoading"
							:actions="actions"
							:search-types="searchTypes"
							:selected-action="selectedAction"
							:selected-search-type="selectedSearchType"
							@on-select-search-type="onSelectSearchType"
							@on-search-change="handleSearch"
							@on-item-search="getResults"
						></database-user-controls>
					</div>
					<div v-if="isStartsWithEndsWith" class="replacement d-flex flex-fill gap-2 p-0">
						<input
							type="text"
							placeholder="Replace Start"
							class="flex-grow-1 fs-14px border border-1 rounded-3"
							v-model="startsWith"
						/>
						<input
							type="text"
							placeholder="Replace End"
							class="flex-grow-1 fs-14px border border-1 rounded-3"
							v-model="endsWith"
						/>
					</div>
					<div v-else class="replacement input d-flex flex-fill w-100 p-0">
						<input
							type="text"
							placeholder="Enter replacement"
							class="flex-grow-1 fs-14px border border-1 rounded-3"
							v-model="databaseReplacement"
						/>
					</div>
				</div>

				<linksy-button variant="white_outline" class="filter-btn-toggle text-nowrap d-none d-lg-flex align-items-center" :class="{active: showFilter}" @click="toggleFilter">
                    <i class="fa-solid fa-bars-staggered me-2"></i><span>Filter</span>
                </linksy-button>
            </div>

			<linksy-database-filters
				v-if="showFilter"
				:filter="filter"
				:data="replacer.data"
				:tables="databaseList"
				:is-contains="isContains"
				@on-change="handleFilterChange"
			>
			</linksy-database-filters>

			<div>
				<linksy-data-table
					class="shadow-sm"
					:columns="columns"
					:data="items"
					:loading="isLoading"
				>
					<template v-slot:column-header-0>		
						<div class="d-flex align-items-center">
							<div
								class="linksy-checkbox me-3"
								:class="{'checked': isAllRowsSelected, 'is-disabled': isProcessing}"
								@click="selectAllRows"
							></div>
							<div>
								Tables <span class="fs-20px linksy-item-count" v-if="!isLoading">{{pagination.total}}</span>
							</div>
						</div>
					</template>

					<template v-slot:column-0="{item, index}">
						<div style="width: 300px;" class="table-name-container">
							<div class="d-flex align-items-center">
								<span class="linksy-checkbox me-3" @click="handleSelect(item, '', '', true)" :class="{'checked': isItemSelected(item, '', '', true)}" />
								<span class="text-overflow-wrap text-primary">{{item.table_name}}</span>
							</div>
							
							<div
								v-if="activeItem === index"
								class="d-flex flex-column mt-3"
							>
								<span class="fs-14px">Columns: <span class="item-count">{{item.columns_results.map(e => e.column_name).length}}</span></span>
								<span class="fs-14px">Results: <span class="item-count">{{totalResults(item.columns_results)}}</span></span>
							</div>
						</div>
					</template>

					<template v-slot:column-1="{item, index}">
						<pages-column
							:item="item"
							:search="search"
							:expand="activeItem == index"
							:isItemSelected="isItemSelected"
							:actions="actions"
							:selectedAction="selectedAction"
							:isApplying="isApplying"
							:isApplyDisabled="isApplyDisabled"
							:isGettingOffset="isGettingOffset"
							@on-toggle="e => handleRowToggle(e, item, index)"
							@on-select-result="(item, col, res) => handleSelect(item, col, res, false)"
							@on-select-all="handleSelectAll"
							@on-close-item="() => activeItem = null"
							@on-show-more="(item) => getOffsetResults(item)"
							@on-select-action="(index) => selectAction(index)"
							@on-apply="handleApplyAction"
							@on-cancel="() => selectedAction = null"
						></pages-column>
					</template>
				</linksy-data-table>
			</div>


            <linksy-pagination
                :pageLength="pageLength"
                :perPage="pagination.limit"
                :currentPage="pagination.page+1"
                :total="pagination.total"
                @on-change-page="handleChangePage"
                @on-change-rows-per-page="handleChangeRowsPerPage"
            >
            </linksy-pagination>
        </div>
    `,
    mounted() {
        this.getDatabaseList()
    },
}
