// Silence may be golden, but sometimes words paint the canvas of understanding.
class DatabaseReplacer {
	constructor(token, filters = {}) {
		if (!token || typeof token !== 'string') {
			throw new TypeError('Token must be a string')
		}

		this.filters = filters
		this.selectedMode = DatabaseReplacer.FILTER_MODES[0].value

		// private methods
		this._token = token
		this._data = []
		this._tables = []
		this._pagination = {
			page: 0,
			limit: 10,
			total: 0,
			filteredTotal: 0,
		}
	}

	static get SUPPORTED_ACTIONS() {
		return [
			{
				value: 'replace',
				label: 'Replace match',
				message: 'The Replace match function replaces the input text with the text provided in the replacement field.',
			},
		]
	}

	static get SEARCH_TYPES() {
		return [
			{
				value: 'CONTAINS',
				label: 'Contains',
				message: 'Search columns that include a specific word(s).',
			},
			{
				value: 'STARTS_ENDS_WITH',
				label: 'Starts/Ends With',
				message: 'Search columns that begins/ends with a specific word(s).',
			},
		]
	}

	static get FILTER_MODES() {
		return [
			{
				placeholder: 'Filter data by title',
				label: 'Filter title',
				value: 'source',
			},
			{
				placeholder: 'Filter data by link',
				label: 'Filter link',
				value: 'link',
			},
		]
	}

	get pagination() {
		return Object.freeze({
			...this._pagination,
			total: this._pagination.filteredTotal || this._pagination.total,
		})
	}

	set pagination(value) {
		if (typeof value !== 'object') {
			throw new Error('Invalid pagination value: ' + value)
		}

		this._pagination = {
			...this._pagination,
			...value,
		}
	}

	get data() {
		return Object.freeze([...this._data])
	}

	get tables() {
		return this._tables
	}

	set data(data) {
		this._data = data
	}

	reset() {
		this._data = []
		this._pagination = {
			...this._pagination,
			page: 0,
			total: 0,
			filteredTotal: 0,
		}
	}

	toPage(page) {
		if (typeof page !== 'number') {
			throw new Error('Invalid pagination page: ' + page)
		}

		this._pagination.page = page
	}

	async searchDatabase(query, searchMode, filterQuery, selectedTables, filter) {
		this._data = []
		
		const params = toQueryString({
			action: 'linksy_search_and_replace_search_db',
			nonce: this._token,
			search: searchMode === 'CONTAINS' ? encodeURIComponent(query.trim()) : JSON.stringify(query),
			page: this._pagination.page,
			limit: this._pagination.limit,
			filter: JSON.stringify(filter),
			filter_mode: this.selectedMode,
			filter_query: filterQuery,
			selected_tables: JSON.stringify(selectedTables),
			search_mode: searchMode || DatabaseReplacer.SEARCH_TYPES[0].value,
		})

		const {
			data: { data },
		} = await axios.get(ajaxurl + '?' + params)

		// update the pagination and data
		this._pagination.total = data.total
		this._data = data.results
		this._tables = data.tables

		return this._data
	}

	async searchColumnOffset(query, searchMode, filter, item) {
		const params = toQueryString({
			action: 'linksy_search_and_replace_search_offset_db',
			nonce: this._token,
			search: searchMode === 'CONTAINS' ? encodeURIComponent(query.trim()) : JSON.stringify(query),
			filter: JSON.stringify(filter),
			item: JSON.stringify(item),
			filter_mode: this.selectedMode,
			search_mode: searchMode || DatabaseReplacer.SEARCH_TYPES[0].value,
		})

		const {
			data: { data },
		} = await axios.get(ajaxurl + '?' + params)

		this._data = this._data.map((table) => {
			if (table.table_name === item.table_name) {
				const columns = table.columns_results.map((col) => {
					if (col.column_name === item.column_name) {
						return {
							...col,
							results: [...col.results, ...data],
							current_results_length: col.current_results_length + data.length
						}
					}
					return col
				})
				return { ...table, columns_results: columns }
			}
			return table
		})

		return this._data
	}

	async applyAction(search, searchMode = 'CONTAINS', action, items = [], replacement = '') {
		if (!items.length) {
			return
		}

		const params = urlSearchParams({
			action: `linksy_search_and_replace_${action}_db`,
			nonce: this._token,
			items: JSON.stringify(items),
			search_mode: searchMode,
			db_replacement: searchMode === 'CONTAINS' ? replacement : JSON.stringify(replacement),
			search: searchMode === 'CONTAINS' ? search : JSON.stringify(search),
		})

		const {data : { data }} = await axios.post(ajaxurl, params)

		return data
	}
}
