// Silence may be golden, but sometimes words paint the canvas of understanding.
const AnchorReplace = {
	name: 'AnchorReplace',
	components: {
		LinksyButton,
		LinksyDataTable,
		LinksyPagination,
		LinkOccurrence,
		SourcesColumn,
		LinksColumn,
		LinksySearchAndReplaceFilter,
		LinksySearchAndReplaceBulkAction,
		LinksySearchAndReplaceUserControls,
		LinksySearchAndReplaceTableHeader,
	},
	directives: {
		'linksy-dom-text': LinksyDOMText,
	},
	data() {
		return {
			query: '',
			search: '',
			filter: {
                search_mode: ['source'],
                source: "",
				link: "",
				anchor: ""
            },
			searchPlaceholder: 'Enter anchor to search',
			isLoading: false,
			anchorReplacement: '',
			selectedItems: [],
			selectedSearchType: 'EXACT',
			fixableSearchTypes: [
				'COMMA_AFTER',
				'COMMA_BEFORE',
				'SPACE_AFTER',
				'SPACE_BEFORE',
				'FULL_STOP_AFTER',
				'FULL_STOP_BEFORE',
			],
			selectedAction: null,
			filterSearch: '',
			replacer: new AnchorReplacer(LINKSY_SECURE_TOKEN),
			isAnchorReplacing: false,
			showFilter: false,
			activeItem: null
		}
	},
	computed: {
		isSourceBased: {
			get() {
				return this.selectedSearchType === 'DUPLICATE' || this.selectedSearchType === 'SHARED_TARGET'
			}
		},
		allColumns: {
			get() {
				return [
					{
						name: this.isSourceBased ? 'Source' : 'Anchor',
					},
					{
						name: 'Source/Target'
					}
				]
			}
		},
		pageLength: {
			get() {
				return Object.keys(this.items).length
			},
		},
		items: {
			get() {
				return this.replacer.data
			},
		},
		pagination: {
			get() {
				return this.replacer.pagination
			},
		},
		isSearchDisabled: {
			get() {
				return [
					'SHARED_TARGET',
					'DUPLICATE',
					'EMPTY_ANCHOR',
					'COMMA_AFTER',
					'COMMA_BEFORE',
					'SPACE_AFTER',
					'SPACE_BEFORE',
					'FULL_STOP_AFTER',
					'FULL_STOP_BEFORE',
				].includes(this.selectedSearchType)
			},
		},
		actions() {
			if (this.fixableSearchTypes.includes(this.selectedSearchType)) {
				return AnchorReplacer.SUPPORTED_ACTIONS.filter(
					(a) => !['replace', 'replace_match', 'remove_match'].includes(a.value),
				)
			} else if (['SHARED_TARGET', 'DUPLICATE'].includes(this.selectedSearchType)) {
				return AnchorReplacer.SUPPORTED_ACTIONS.filter((a) => a.value === 'unlink')
			}

			return AnchorReplacer.SUPPORTED_ACTIONS.filter((a) => a.value !== 'fix')
		},
		searchTypes: {
			get() {
				return AnchorReplacer.SEARCH_TYPES
			},
		},
		filterModes: {
			get() {
				return AnchorReplacer.FILTER_MODES
			},
		},
		isAllRowsSelected() {
			if (this.items.length === 0) return false;
			if (this.selectedItems.length === 0) return false;
			let totalLinks = 0;
		  
			if (!this.isSourceBased) {
			  this.items.forEach(item => {
				item.sources.forEach(source => {
				  totalLinks += source.links.length;
				});
			  });
			} else {
				this.items.forEach(item => {
					totalLinks += item.anchors.length;
				});
			}
			
			return this.selectedItems.length === totalLinks;
		},
		isDisabled() {
			return (
				this.fixableSearchTypes.includes(this.selectedSearchType) ||
				['remove_match', 'fix', 'unlink', 'retain_best'].includes(this.selectedAction)
			)
		},
	},
	watch: {
		selectedAction: {
			handler: function (val, oldVal) {
				if (oldVal === 'retain_best') {
					this.selectedItems = []
				}
			},
		},
	},
	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
				if (this.selectedAction === 'retain_best') {
					this.selectedItems = this.getInferiorAnchors()
				}
			}
		},
		handleSearch(q) {
			if (q == '') {
				this.replacer.reset()
			}

			if (this.search !== q) {
				this.search = q
			}
		},
		handleCancel() {
			this.selectedAction = null
		},
		handleChangePage(page) {
			this.replacer.toPage(page - 1)
			this.getAnchors()
		},
		handleChangeRowsPerPage(limit) {
			if (limit != this.pagination.limit) {
				this.replacer.pagination = { limit }
				this.getAnchors()
			}
		},
		async getAnchors() {
			if (!this.isSearchDisabled && this.search === '') {
				return
			}

			this.selectedItems = []

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

				const searchType = this.searchTypes.find((e) => e.value === this.selectedSearchType)
				await this.replacer.searchAnchors(this.search, searchType.value, this.filterSearch, this.filter)
			} catch (error) {
				this.$toast.error(error.response?.data?.data || error.message)
			} finally {
				this.isLoading = false
			}
		},
		async replaceAnchor(replacement, item, row) {
			this.isAnchorReplacing = true
			const newRow = { ...row, anchors: [item] }

			try {
				const stat = await this.replacer.applySelectedAction(
					this.search,
					'replace',
					[newRow],
					replacement
				)

				this.$toast.success('Anchor edited successfully!')
				item.anchor = replacement
				item.isEditingAnchor = false

				this.isAnchorReplacing = false
				this.getAnchors()
			} catch (error) {
				this.$toast.error(error.response?.data?.data || error.message)
			}
			
		},
		async applyAction(s, r) {
			s()
			r()

			try {
				if (this.search !== this.query) {
					this.$toast.error('Search parameter has changed')

					return
				}

				if (this.selectedItems.length === 0) {
					this.$toast.error('No anchor to process')
					return
				} else {
					this.isLoading = true

					const stats = await this.replacer.applySelectedAction(
						this.search,
						this.selectedAction,
						this.selectedItems,
						this.anchorReplacement,
					)

					let message = ''
					const actions = {
						replace: 'replaced',
						replace_match: 'replaced',
						remove_match: 'keyword(s) deleted',
						fix: 'fixed',
						unlink: 'unlinked',
						retain_best: 'deleted',
					}
					
					message = `${this.selectedItems.length - stats.failed.length} of ${this.selectedItems.length} anchor(s) ${actions[this.selectedAction]}`
					
					stats.failed.length === this.selectedItems.length ? this.$toast.error(message) : this.$toast.success(message);

					this.getAnchors()
				}
			} catch (error) {
				this.$toast.error(error.response?.data?.data || error.message)
			} finally {
				this.isLoading = false
			}
		},
		handleFilterChange(values) {
			this.filter = { ...values }
			this.replacer.reset()

			this.getAnchors()
		},
		handleAnchorSearch() {
			if (this.search || this.isSearchDisabled) {
				this.selectedItems = []
				this.getAnchors()
			}
		},
		onSelectSearchType(label) {
			const type = this.searchTypes.find((type) => type.value === label)
			if (type) {
				this.selectedSearchType = type.value

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

				if (!['EXACT', 'ANCHOR_CONTAINS', 'STARTS_WITH', 'ENDS_WITH'].includes(type.value)) {
					this.getAnchors()
				}		
			}
		},

		selectAllRows() {
			if (this.items.length === 0) return;
		  
			if (this.isAllRowsSelected) {
				this.selectedItems = [];
			} else {
				this.selectedItems = [];

				if (this.isSourceBased) {
					this.items.forEach(item => {
						const post_id = item.post_id;
						item.anchors.forEach(anchorObj => {
							this.selectedItems.push({
								post_id: post_id,
								anchor: anchorObj.anchor,
								link: anchorObj.link,
							});
						})
					});
				} else {
					this.items.forEach(item => {
						const anchorText = item.anchor;
						item.sources.forEach(source => {
							source.links.forEach(linkObj => {
								this.selectedItems.push({
									post_id: source.post_id,
									link: linkObj.link,
									anchor: anchorText,
								});
							});
						});
					});
				}
			}
		},
		
		isItemSelected(item, anchor, isTableBased) {
			if (isTableBased) {
				return item.anchors.every(itemAnchor =>
					this.selectedItems.some(selected =>
						selected.post_id === item.post_id &&
						selected.anchor === itemAnchor.anchor &&
						selected.link === itemAnchor.link
					)
				);
			} else {
				return this.selectedItems.some(selected =>
					selected.post_id === item.post_id &&
					selected.anchor === anchor.anchor &&
					selected.link === anchor.link
				);
			}
		},

		handleSelectAll(item) {
			const allAnchorsSelected = item.anchors.every(anchor =>
			  this.selectedItems.some(selected =>
				selected.post_id === item.post_id &&
				selected.anchor === anchor.anchor &&
				selected.link === anchor.link
			  )
			);
		  
			if (allAnchorsSelected) {
			  item.anchors.forEach(anchor => {
				const selectedIndex = this.selectedItems.findIndex(selected =>
				  selected.post_id === item.post_id &&
				  selected.anchor === anchor.anchor &&
				  selected.link === anchor.link
				);
				if (selectedIndex !== -1) {
				  this.selectedItems.splice(selectedIndex, 1);
				}
			  });
			} else {
			  item.anchors.forEach(anchor => {
				const isAlreadySelected = this.selectedItems.some(selected =>
				  selected.post_id === item.post_id &&
				  selected.anchor === anchor.anchor &&
				  selected.link === anchor.link
				);
				if (!isAlreadySelected) {
				  this.selectedItems.push({
					post_id: item.post_id,
					anchor: anchor.anchor,
					link: anchor.link
				  });
				}
			  });
			}
		},

		handleSelect(item, anchor, isTableBased) {
			if (isTableBased) {
			  const allSelected = this.isItemSelected(item, null, true);
			  item.anchors.forEach(itemAnchor => {
				const selectedIndex = this.selectedItems.findIndex(selected =>
				  selected.post_id === item.post_id &&
				  selected.anchor === itemAnchor.anchor &&
				  selected.link === itemAnchor.link
				);
				if (!allSelected && selectedIndex === -1) {
				  this.selectedItems.push({
					post_id: item.post_id,
					anchor: itemAnchor.anchor,
					link: itemAnchor.link
				  });
				} else if (allSelected && selectedIndex !== -1) {
				  this.selectedItems.splice(selectedIndex, 1);
				} else if (selectedIndex !== -1 && allSelected) {
				  this.selectedItems.splice(selectedIndex, 1);
				}
			  });
			} else {
			  const selectedIndex = this.selectedItems.findIndex(selected =>
				selected.post_id === item.post_id &&
				selected.anchor === anchor.anchor &&
				selected.link === anchor.link
			  );
			  if (selectedIndex === -1) {
				this.selectedItems.push({
				  post_id: item.post_id,
				  anchor: anchor.anchor,
				  link: anchor.link
				});
			  } else {
				this.selectedItems.splice(selectedIndex, 1);
			  }
			}
		},

		isDefItemSelected(item, source, isTableBased) {
			if (isTableBased) {
			  return item.sources.every(itemSource =>
				itemSource.links.every(itemLink =>
				  this.selectedItems.some(selected =>
					selected.post_id === itemSource.post_id &&
					selected.anchor === item.anchor &&
					selected.link === itemLink.link
				  )
				)
			  );
			} else {
			  return source.links.some(link =>
				this.selectedItems.some(selected =>
				  selected.post_id === source.post_id &&
				  selected.anchor === item.anchor &&
				  selected.link === link.link
				)
			  );
			}
		},
		handleDefSelectAll(item) {
			item.sources.forEach(source => {
			  const allLinksSelectedInSource = source.links.every(link =>
				this.selectedItems.some(selected =>
				  selected.post_id === source.post_id &&
				  selected.anchor === item.anchor &&
				  selected.link === link.link
				)
			  );
		  
			  if (allLinksSelectedInSource) {
				source.links.forEach(link => {
				  const selectedIndex = this.selectedItems.findIndex(selected =>
					selected.post_id === source.post_id &&
					selected.anchor === item.anchor &&
					selected.link === link.link
				  );
				  if (selectedIndex !== -1) {
					this.selectedItems.splice(selectedIndex, 1);
				  }
				});
			  } else {
				source.links.forEach(link => {
				  const isAlreadySelected = this.selectedItems.some(selected =>
					selected.post_id === source.post_id &&
					selected.anchor === item.anchor &&
					selected.link === link.link
				  );
				  if (!isAlreadySelected) {
					this.selectedItems.push({
					  post_id: source.post_id,
					  anchor: item.anchor,
					  link: link.link
					});
				  }
				});
			  }
			});
		},
		handleDefSelect(item, source, isTableBased) {
			if (isTableBased) {
			  const allSelected = this.isDefItemSelected(item, null, true);
			  item.sources.forEach(itemSource => {
				itemSource.links.forEach(itemLink => {
				  const selectedIndex = this.selectedItems.findIndex(selected =>
					selected.post_id === itemSource.post_id &&
					selected.anchor === item.anchor &&
					selected.link === itemLink.link
				  );
				  if (!allSelected && selectedIndex === -1) {
					this.selectedItems.push({
					  post_id: itemSource.post_id,
					  anchor: item.anchor,
					  link: itemLink.link
					});
				  } else if (allSelected && selectedIndex !== -1) {
					this.selectedItems.splice(selectedIndex, 1);
				  }
				});
			  });
			} else {
			  const allSourceLinksSelected = this.isDefItemSelected(item, source, false);
			  source.links.forEach(link => {
				const selectedIndex = this.selectedItems.findIndex(selected =>
				  selected.post_id === source.post_id &&
				  selected.anchor === item.anchor &&
				  selected.link === link.link
				);
				if (!allSourceLinksSelected && selectedIndex === -1) {
				  this.selectedItems.push({
					post_id: source.post_id,
					anchor: item.anchor,
					link: link.link
				  });
				} else if (allSourceLinksSelected && selectedIndex !== -1) {
				  this.selectedItems.splice(selectedIndex, 1);
				}
			  });
			}
		},		

		getInferiorAnchors() {
			const items = []

			for (let item of this.items) {
				if (Array.isArray(item)) {
					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
		},

		handleRowToggle(index) {
			this.activeItem = index
		},
	},
	template: `
        <div id="anchor-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">
					<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="handleAnchorSearch"
					/>
					
					<div class="replacement d-flex flex-fill w-100 p-0">
						<input
							type="text"
							:placeholder=" selectedSearchType === 'ANCHOR_CONTAINS' ? 'Enter replacement word or anchor' : 'Enter replacement anchor' "
							class="flex-grow-1 fs-14px border border-1 rounded-3"
							:class="{ 'is-disabled': isDisabled }"
							v-model="anchorReplacement"
						/>
					</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="anchor"
				: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="Anchor"
                        :is-all-rows-selected="isAllRowsSelected"
                        :select-all-rows="selectAllRows"
                        :pagination="pagination"
                    />
                </template>

				<template v-slot:column-0="{ item, index }">
					<sources-column
						style="min-width: 600px;"
						v-if="selectedSearchType === 'SHARED_TARGET'"
						is-selectable-anchor
						:item="item"
						:isItemSelected="isItemSelected"
						@on-select="handleSelect"
						@on-select-all="handleSelectAll"
					></sources-column>

					<div v-else class="first-column">
						<div v-if="selectedSearchType === 'DUPLICATE'" class="d-flex align-items-center" @click="handleSelect(item, null, true)">
							<span class="linksy-checkbox me-3" :class="{'checked': isItemSelected(item, null, true)}" />
							<h4 class="m-0 text-overflow-wrap fw-medium" v-linksy-dom-text>"{{item.anchors[0].anchor}}"</h4>
						</div>

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

				<template v-slot:column-1="{ item, index }">
					<div v-if="isSourceBased">
						<div v-if="selectedSearchType === 'SHARED_TARGET'" class="sources-targets-container d-flex flex-column border border-1 rounded-3">
							<div class="item-title position-relative p-3 w-100">
								<h4 class="m-0 text-overflow-wrap fw-medium">{{item.source}}</h4>
								<div class="item-direct-links position-absolute">
									<a :href="item.view_link" target="_blank">View</a>
									<a :href="item.edit_link" target="_blank">Edit</a>
								</div>
							</div>

							<h5 class="target-text fs-14px">target</h5>

							<a :href="item.anchors[0].link" target="_blank" class="fs-16px text-primary text-wrap text-decoration-none p-3">
								{{ item.anchors[0].link }}
							</a>
						</div>

						<sources-column
							v-else
							:selectedSearchType="selectedSearchType"
							is-selectable-link
							:item="item"
							:isItemSelected="isItemSelected"
							@on-select="handleSelect"
							@on-select-all="handleSelectAll"
						></sources-column>
					</div>

					<links-column
						v-else
						:key="index"
						is-anchor-replace
						: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>
    `,
}
