// Silence may be golden, but sometimes words paint the canvas of understanding.
const LinkReplace = {
	name: 'LinkReplace',
	directives: {
		'linksy-tooltip': LinksyTooltip,
	},
	components: {
		LinksyButton,
		LinksyDropdown,
		LinksyDataTable,
		LinksyPagination,
		SourcesColumn,
		LinksColumn,
		LinksySearchAndReplaceFilter,
		LinksySearchAndReplaceBulkAction,
		LinksySearchAndReplaceTableHeader,
		LinksySearchAndReplaceUserControls,
		LinkOccurrence,
	},
	data() {
		return {
			query: '',
			search: '',
			filter: {
                search_mode: ['source'],
                source: "",
				link: "",
				anchor: ""
            },
			searchPlaceholder: 'Enter link to search',
			columns: [
				{
					name: 'Pages',
				},
			],
			isLoading: false,
			linkReplacement: '',
			selectedItems: [],
			selectedSearchType: 'EXACT',
			selectedAction: null,
			filterSearch: '',
			replacer: new LinkReplacer(LINKSY_SECURE_TOKEN),
			showNotice: false,
			isProcessing: false,
			failedToProcess: false,
			isLinkReplacing: false,
			showFilter: false,
			activeItem: null
		}
	},
	computed: {
		allColumns: {
			get() {
				return [
					{
						name: 'Link',
						format: (row) => row.source
					},
					{
						name: this.selectedSearchType === 'TWO_WAY' ? 'Reciprocal' : 'Source'
					}
				]
			}
		},
		pageLength: {
			get() {
				return Object.keys(this.items).length
			},
		},
		items: {
			get() {
				return this.replacer.data
			},
		},
		pagination: {
			get() {
				return this.replacer.pagination
			},
		},
		isSearchDisabled: {
			get() {
				return !['EXACT', 'CONTAINS'].includes(this.selectedSearchType)
			},
		},
		isAllRowsSelected: {
			get() {
				if (this.items.length === 0) return false

				if (this.selectedSearchType === 'TWO_WAY') {
					const itemsCount = this.items.reduce((acc, item) => acc + item.length, 0)
					return itemsCount === this.selectedItems.length
				}

				const sourcesCount = this.items.reduce((acc, item) => acc + item.sources.length, 0);
				return sourcesCount === this.selectedItems.length;
			},
		},
		actions: {
			get() {
				if (this.isSearchDisabled) {
					return LinkReplacer.SUPPORTED_ACTIONS.filter((a) => ['unlink', 'retain_best'].includes(a.value))
				} else if (this.selectedSearchType === 'CONTAINS') {
					return LinkReplacer.SUPPORTED_ACTIONS.filter((a) => !['replace', 'retain_best'].includes(a.value))
				} else if (this.selectedSearchType === 'EXACT') {
					return LinkReplacer.SUPPORTED_ACTIONS.filter((a) => ['replace', 'unlink'].includes(a.value))
				}

				return LinkReplacer.SUPPORTED_ACTIONS.filter((a) => a.value !== 'retain_best')
			},
		},
		searchTypes: {
			get() {
				return LinkReplacer.SEARCH_TYPES
			},
		},
		filterModes: {
			get() {
				return LinkReplacer.FILTER_MODES
			},
		},
	},
	methods: {
		toggleFilter() {
            this.showFilter = !this.showFilter
        },
		selectAction(index) {
			let action = Object.values(this.actions)[index]
			if (action.value === this.selectedAction) {
				this.selectedAction = null
			} else {
				this.selectedAction = action?.value
			}
		},
		async replaceLink(replacement, row) {
			this.isLinkReplacing = true

			try {
				const stat = await this.replacer.applyAction(
					this.search,
					'replace',
					row,
					replacement
				)

				this.$toast.success('Link edited successfully!')
				row.link = replacement
				row.editing = false

				this.isLinkReplacing = false
				this.getLinks()
			} catch (error) {
				this.$toast.error(error.response?.data?.data || error.message)
			}
			
		},
		applyAction(s, r) {
			if (this.search !== this.query) {
				this.$toast.error('Search parameter has changed')

				return
			}

			if (['replace_match', 'replace'].includes(this.selectedAction) && this.linkReplacement === '') {
				this.$toast.error('Replacement field cannot be empty')
				return
			}

			if (this.selectedAction === 'retain_best') {
				this.selectedItems = this.getInferiorAnchors(this.selectedItems);
			}
			
			if (this.selectedItems.length === 0) {
				this.$toast.error('No link to process')
				return
			} else {
				this.isLoading = true

				setTimeout(() => {
					this.isLoading = false
					this.failedToProcess = false
					this.$toast.info('Links will be processed in the background')
					this.handleApplyAction()
					s()
					r()
				}, 1500);
			}
		},
		async handleApplyAction() {
			this.showNotice = true
			this.isProcessing = true

			try {
				
				let report = {
					failed: []
				};
				
				['retain_best', 'unlink'].includes(this.selectedAction) ? report.deleted = [] : report.replaced = [];
				
				const actions = {
					replace: 'replaced',
					replace_match: 'updated',
					remove_match: 'updated',
					unlink: 'unlinked',
					retain_best: 'deleted',
				};
				
				for (const item of this.selectedItems) {
					const stats = await this.replacer.applyAction(
						this.search,
						this.selectedAction,
						item,
						this.linkReplacement
					);
				
					if (['retain_best', 'unlink'].includes(this.selectedAction)) {
						const { deleted, failed } = stats;
						report.deleted.push(...deleted);
						report.failed.push(...failed);
					} else {
						const { replaced, failed } = stats;
						report.replaced.push(...replaced);
						report.failed.push(...failed);
					}
				}
				
				const success = this.selectedAction === 'retain_best' || this.selectedAction === 'unlink' ? 'deleted' : 'replaced';
				const message = `${report[success].length} of ${report[success].length + report.failed.length} link(s) have been ${actions[this.selectedAction]}.`;
				
				if (report[success].length === 0) {
					this.$toast.error(message);
					this.failedToProcess = true;
				} else {
					this.$toast.success(message);
					this.selectedItems = [];
				}				
				
			} catch (error) {
				this.$toast.error(error.response?.data?.data || error.message)
			} finally {
				this.isProcessing = false
			}
		},
		refreshTable() {
			this.showNotice = false
			this.failedToProcess = false
			this.linkReplacement = ''

			this.getLinks()
		},

		async getLinks() {
			if (!this.search && !this.isSearchDisabled) return
			this.selectedItems = []

			try {
				this.query = this.search
				this.isLoading = true

				await this.replacer.searchLinks(this.search, this.selectedSearchType, this.filterSearch, this.filter)
			} catch (error) {
				this.$toast.error(error.response?.data?.data || error.message)
			} finally {
				this.isLoading = false
			}
		},

		// others
		handleFilterChange(values) {
			this.filter = { ...values }
			this.replacer.reset()

			this.getLinks()
		},
		handleLinkSearch() {
			if (this.search || this.isSearchDisabled) {
				this.getLinks()
			}
		},
		handleSearch(q) {
			if (q == '') {
				this.replacer.reset()
			}

			if (this.search !== q) {
				this.search = q
			}
		},
		handleFilterSearch(q) {
			this.filterSearch = q
			this.getLinks()
		},
		handleCancel() {
			this.selectedAction = null
		},
		handleChangePage(page) {
			this.replacer.toPage(page - 1)
			this.getLinks()
		},
		handleChangeRowsPerPage(limit) {
			if (limit != this.pagination.limit) {
				this.replacer.pagination = { limit }
				this.getLinks()
			}
		},

		selectAllRows() {
			if (this.items.length === 0) return;
		
			if (this.isAllRowsSelected) {
				this.selectedItems = [];
			} else {
				if (this.selectedSearchType === 'TWO_WAY') {
					this.selectedItems = this.items.flat(1).map(item => ({ ...item }));
				} else {
					this.selectedItems = []
					this.items.forEach(item => {
						item.sources.forEach(source => {
							this.selectedItems.push({ ...source });
						});
					});
				}
			}
		},
		
		isItemSelected(item, anchor, isTableBased) {
			if (isTableBased) {
				const selectedItem = this.selectedItems.find(selected => selected.post_id === item.post_id && selected.link === item.link);
				if (!selectedItem) return false;
		
				return Array.isArray(selectedItem.anchors) && selectedItem?.anchors.length === item.anchors.length;
			} else {
				const selectedItem = this.selectedItems.find(selected => selected.post_id === item.post_id && selected.link === item.link);
				if (!selectedItem) return false;
				return selectedItem.anchors?.some(selectedAnchor =>
					selectedAnchor.anchor === anchor.anchor
				) || false;
			}
		},
		
		handleSelect(item, anchor, isTableBased) {
			
			if (isTableBased) {
				const existingIndex = this.selectedItems.findIndex(selected => selected.post_id === item.post_id && selected.link === item.link);
				if (existingIndex > -1) {
					this.selectedItems.splice(existingIndex, 1);
				} else {
					this.selectedItems.push(item);
				}
			} else {
				const selectedItemIndex = this.selectedItems.findIndex(selected => selected.post_id === item.post_id && selected.link === item.link);
				if (selectedItemIndex > -1) {
					const selectedItem = this.selectedItems[selectedItemIndex];
					if (!selectedItem.anchors) {
						selectedItem.anchors = [];
					}
					const anchorIndex = selectedItem.anchors.findIndex(
						selAnchor => selAnchor.anchor === anchor.anchor
					);
					if (anchorIndex > -1) {
						selectedItem.anchors.splice(anchorIndex, 1);
						if (selectedItem.anchors.length === 0) {
							this.selectedItems.splice(selectedItemIndex, 1);
						}
					} else {
						selectedItem.anchors.push(anchor);
					}
				} else {
					this.selectedItems.push({ ...item, anchors: [anchor] });
				}
			}
		},
		
		handleSelectAll(item) {
			const isAllSelected = this.selectedItems.some(selected =>
				selected.post_id === item.post_id && selected.link === item.link &&
				selected.anchors?.length === item.anchors.length
			);
		
			const selectedItemIndex = this.selectedItems.findIndex(selected => selected.post_id === item.post_id && selected.link === item.link);
		
			if (isAllSelected) {
				if (selectedItemIndex > -1) {
					const selectedItem = this.selectedItems[selectedItemIndex];
					if (selectedItem.anchors) {
						delete selectedItem.anchors;
						
						const { anchors: omittedAnchor, ...selectedItemWithoutAnchors } = selectedItem;
						const { anchors: originalOmittedAnchor, ...originalItemWithoutAnchors } = item;
		
						if (JSON.stringify(selectedItemWithoutAnchors) === JSON.stringify(originalItemWithoutAnchors)) {
							this.selectedItems.splice(selectedItemIndex, 1);
						}
					}
				}
			} else {
				if (selectedItemIndex > -1) {
					this.selectedItems[selectedItemIndex].anchors = [...item.anchors];
				} else {
					this.selectedItems.push({ ...item, anchors: [...item.anchors] });
				}
			}
		},

		isDefItemSelected(item, source, isTableBased) {
			if (isTableBased) {
				return item.sources.every(originalSource =>
					this.selectedItems.some(selectedSource =>
						selectedSource.post_id === originalSource.post_id && selectedSource.link === item.link
					)
				);
			} else {
				return this.selectedItems.some(selectedSource =>
					selectedSource.post_id === source.post_id &&
					selectedSource.link === item.link
				);
			}
		},
		
		handleDefSelect(item, source, isTableBased) {
			if (isTableBased) {
				const allSourcesOfItemAreSelected = item.sources.every(originalSource =>
				  this.selectedItems.some(
					selectedSource =>
					  selectedSource.post_id === originalSource.post_id && selectedSource.link === item.link
				  )
				);
			
				if (allSourcesOfItemAreSelected) {
				  item.sources.forEach(originalSource => {
					const indexToRemove = this.selectedItems.findIndex(
					  selectedSource =>
						selectedSource.post_id === originalSource.post_id && selectedSource.link === item.link
					);
					if (indexToRemove > -1) {
					  this.selectedItems.splice(indexToRemove, 1);
					}
				  });
				} else {
				  item.sources.forEach(originalSource => {
					const selectedItem = { ...originalSource, link: item.link };
					const isAlreadySelected = this.selectedItems.some(
					  selected => selected.post_id === selectedItem.post_id && selected.link === selectedItem.link
					);
			
					if (!isAlreadySelected) {
					  this.selectedItems.push(selectedItem);
					}
				  });
				}
			} else {
			  const selectedSource = { ...source, link: item.link };
			  const isAlreadySelected = this.selectedItems.some(
				selected =>
					selected.post_id === selectedSource.post_id &&
					selected.link === selectedSource.link &&
					selected.anchors?.some(
						selAnchor => selAnchor.anchor === selectedSource.anchors?.[0]?.anchor
					)
			  );
		  
			  if (isAlreadySelected) {
				const indexToRemove = this.selectedItems.findIndex(
					selected =>
						selected.post_id === selectedSource.post_id &&
						selected.link === selectedSource.link &&
						selected.anchors?.some(
							selAnchor => selAnchor.anchor === selectedSource.anchors?.[0]?.anchor
						)
				);
				if (indexToRemove > -1) {
					this.selectedItems.splice(indexToRemove, 1);
				}
			  } else {
				this.selectedItems.push(selectedSource);
			  }
			}
		},
		
		handleDefSelectAll(item) {
			const allSourcesSelected = item.sources.every(originalSource =>
			  this.selectedItems.some(
				selectedSource =>
				  selectedSource.post_id === originalSource.post_id && selectedSource.link === item.link
			  )
			);
		  
			if (allSourcesSelected) {
			  item.sources.forEach(originalSource => {
				const indexToRemove = this.selectedItems.findIndex(
				  selectedSource =>
					selectedSource.post_id === originalSource.post_id && selectedSource.link === item.link
				);
				if (indexToRemove > -1) {
				  this.selectedItems.splice(indexToRemove, 1);
				}
			  });
			} else {
			  item.sources.forEach(originalSource => {
				const selectedItem = { ...originalSource, link: item.link };
				const isAlreadySelected = this.selectedItems.some(
				  selectedSource =>
					selectedSource.post_id === selectedItem.post_id && selectedSource.link === selectedItem.link
				);
				if (!isAlreadySelected) {
				  this.selectedItems.push(selectedItem);
				}
			  });
			}
		},

		onSelectSearchType(type) {
			this.selectedSearchType = type

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

			if (!['EXACT', 'CONTAINS'].includes(this.selectedSearchType)) {
				this.getLinks()
			}
		},
		getInferiorAnchors(selectedItems) {
			const items = []
			
			for (let item of this.items) {
				if (Array.isArray(item)) {
					const groupItems = item.filter(e => {
						return selectedItems.some(a =>
							a.post_id === e.post_id &&
							a.to_post_id === e.to_post_id
						);
					});

					if (groupItems.length === 0) {
						continue;
					}

					const links = item
						.map((e) => {
							const anchors = e.anchors
								.filter((a) => a.score !== null)
								.sort((a, b) => {
									return parseFloat(b.score) - parseFloat(a.score)
								})
							return { ...e, anchors }
						})
						.filter((link) => link.anchors.length > 0)
						.sort((a, b) => {
							return parseFloat(b.anchors?.[0].score) - parseFloat(a.anchors?.[0].score)
						})

					if (links.length > 0) {
						items.push(...links.slice(1))
					}
				} else {
					const anchors = item.anchors.filter((a) => a.score !== null).sort((a, b) => b.score - a.score)
					if (anchors.length > 0) {
						items.push({ ...item, anchors: anchors.slice(1) })
					}
				}
			}

			return items
		},
	},
	template: `
        <div id="link-replace" class="d-flex flex-column gap-4">
			<div v-if="showNotice" class="notice d-flex justify-content-between align-items-center border-success m-0 p-2" :class="['border-' + (failedToProcess ? 'danger' : 'success')]">
				<span v-if="isProcessing">Do not refresh page. Processing Links...</span>
				<div v-else>
					<span v-if="!failedToProcess">Links have been processed successfully. <span class="text-decoration-underline text-primary cursor-pointer" @click="refreshTable">Refresh table</span></span>
					<span v-else>Failed to process links. Ensure link is not running as an autolink</span>
				</div>
				<i class="fa fa-xmark fs-6 text-danger cursor-pointer" @click="() => showNotice = false"></i>
			</div>

			<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">
					<LinksySearchAndReplaceBulkAction
						:is-loading="isLoading"
						:actions="actions"
						:selected-action="selectedAction"
						@on-apply-action="applyAction"
						@select-action="selectAction"
						@on-cancel="handleCancel"
					/>
					<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">
					<LinksySearchAndReplaceUserControls
						:search-types="searchTypes"
						:is-search-disabled="isSearchDisabled"
						:search-placeholder=" selectedSearchType === 'ANCHOR_CONTAINS' ? 'Enter word or anchor to search' : searchPlaceholder "
						@on-select-search-type="onSelectSearchType"
						@on-search-change="handleSearch"
						@on-item-search="handleLinkSearch"
					/>
					<div class="replacement d-flex flex-fill w-100 p-0">
						<input
							type="text"
							:placeholder=" selectedSearchType === 'CONTAINS' ? 'Enter replacement word or link' : 'Enter replacement link' "
							class="flex-grow-1 fs-14px border border-1 rounded-3"
							:class="{'is-disabled': (!['EXACT', 'CONTAINS'].includes(selectedSearchType))}"
							v-model="linkReplacement"
						/>
					</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-search-and-replace-filter
				v-if="showFilter"
				replace-type="link"
				:filter="filter"
				:filter-modes="filterModes"
				@on-change="handleFilterChange"
			>
			</linksy-search-and-replace-filter>

			<linksy-data-table
				class="sar-plain-table overflow-auto shadow-sm"
				:columns="allColumns"
				:data="items"
				:loading="isLoading"
			>
				<template v-slot:column-header-0 class="column-header-0">
                    <LinksySearchAndReplaceTableHeader
						header-name="Link"
                        :is-all-rows-selected="isAllRowsSelected"
                        :select-all-rows="selectAllRows"
                        :pagination="pagination"
                    />
                </template>

				<template v-slot:column-0="{ item, index }">
					<div v-if="selectedSearchType === 'TWO_WAY'" class="d-flex gap-3 align-items-center">
						<sources-column
							class="w-100"
							is-selectable-link
							:item="item[0]"
							:isItemSelected="isItemSelected"
							@on-select="handleSelect"
							@on-select-all="handleSelectAll"
						></sources-column>

						<i class="fa-solid fa-arrow-right-arrow-left fs-6" style="color: #797979;"></i>
					</div>

					<div v-else>
						<div class="d-flex align-items-center" @click="handleDefSelect(item, '', true)">
							<span class="linksy-checkbox me-3" :class="{'checked': isDefItemSelected(item, '', true)}" />
							<h4 class="m-0 text-overflow-wrap fw-medium fs-16px text-primary cursor-pointer">{{item.link}}</h4>
						</div>
					</div>
				</template>

				<template v-slot:column-1="{ item, index }">
					<sources-column
						v-if="selectedSearchType === 'TWO_WAY'"
						enable-title
						is-selectable-link
						class="w-100"
						:item="item[1]"
						:isItemSelected="isItemSelected"
						@on-select="handleSelect"
						@on-select-all="handleSelectAll"
					></sources-column>

					<links-column
						v-else
						:item="item"
						:active="item.sources.length === 1 ? true : activeItem === index"
						:isItemSelected="isDefItemSelected"
						@on-select="handleDefSelect"
						@on-select-all="handleDefSelectAll"
						@on-close-item="activeItem = null"
						@on-open-item="activeItem = index"
					></links-column>
				</template>
			</linksy-data-table>

            <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>
    `,
}
