/**
 * @file assets/js/adminjs/mbs-admin-csv-import.js
 * Two-step CSV / XLSX / ZIP import (upload → map columns → insert) with inline
 * result reporting on Settings → Import / Export.
 *
 * Uses MBSImportProgress (mbs-import-progress.js) for the vanishing-message
 * progress bar UX during upload and import phases.
 *
 * @package MyBookShowroom
 * @since   1.0.0
 */

/* global MBSImportProgress */
( function ( $ ) {
	'use strict';

	var __ = wp.i18n.__;
	var _n = wp.i18n._n;

	// ── State ───────────────────────────────────────────────────────────────────────────
	//
	// currentToken        — transient key returned by mbs_import_csv_headers.
	// currentColumns      — CSV column names from the last successful upload.
	// currentNonce        — nonce captured from the active file input element.
	// currentAreaSelector — jQuery selector for the active import container.
	// mappingActive       — true while the column-mapping UI is visible; used by
	//                       the upload guard to warn before discarding the session.

	var currentToken        = null;
	var currentColumns      = [];
	var currentNonce        = null;
	var currentAreaSelector = '#mbs-csv-import-area';
	var mappingActive       = false;
	var progress            = null;

	$( function () {
		progress = MBSImportProgress.create( {
			barWrap    : document.getElementById( 'mbs-csv-progress-bar' ),
			barFill    : document.getElementById( 'mbs-csv-bar' ),
			msgArea    : document.getElementById( 'mbs-csv-msgarea' ),
			statusArea : document.getElementById( 'mbs-csv-status' ),
		} );
	} );

	// ── Upload ────────────────────────────────────────────────────────────────────────────

	$( '#mbs-csv-upload-btn' ).on( 'click', function () {
		handleUpload( $( this ), '#mbs-csv-file', '#mbs-csv-import-area', __( 'CSV or XLSX', 'my-book-showroom' ) );
	} );

	/**
	 * Shared upload handler for CSV/XLSX and ZIP panels.
	 *
	 * Sets currentNonce and currentAreaSelector so all downstream functions
	 * (showMappingUI, executeImport, renderResultPanel, mbsCsvMessage)
	 * operate on the correct panel without hardcoding element IDs.
	 *
	 * @param {jQuery} $btn          The upload button element.
	 * @param {string} fileInputId   Selector for the file <input>.
	 * @param {string} areaSelector  Selector for the import container div.
	 * @param {string} fileLabel     Human-readable file type label (for messages).
	 */
	function handleUpload( $btn, fileInputId, areaSelector, fileLabel ) {

		// Upload guard: warn before silently discarding an active mapping session.
		if ( mappingActive ) {
			if ( ! window.confirm( __( 'Starting a new upload will discard the current column mapping. Continue?', 'my-book-showroom' ) ) ) {
				return;
			}
			resetImportState();
		}

		var $file = $( fileInputId );
		var nonce = $file.data( 'nonce' );
		var file  = $file[0].files[0];

		if ( ! file ) {
			currentAreaSelector = areaSelector;
			mbsCsvMessage( wp.i18n.sprintf( __( 'Please select a %s file first.', 'my-book-showroom' ), fileLabel ), 'error' );
			return;
		}

		// Capture state for all downstream handlers.
		currentNonce        = nonce;
		currentAreaSelector = areaSelector;

		var originalText = $btn.text();
		var formData = new FormData();
		formData.append( 'action',   'mbs_import_csv_headers' );
		formData.append( 'nonce',    nonce );
		formData.append( 'csv_file', file );

		$btn.prop( 'disabled', true ).text( __( 'Uploading…', 'my-book-showroom' ) );
		mbsCsvMessage( '', '' ); // clear any previous message
		if ( progress ) {
			progress.reset();
			progress.show();
			progress.setBar( 5 );
			progress.setMessage( __( 'Uploading file…', 'my-book-showroom' ) );
		}

		$.ajax( {
			url:         MBS_Settings.ajaxUrl,
			type:        'POST',
			data:        formData,
			processData: false,
			contentType: false,
			success: function ( response ) {
				if ( response.success ) {
					// Handle multi-tab XLSX import result (Sprint IEF-XLSX Phase 4).
				if ( response.data && response.data.multi_tab ) {
					var msg = response.data.message || 'Import complete.';
					if ( response.data.errors && response.data.errors.length ) {
						msg += ' Warnings: ' + response.data.errors.join( ' ' );
					}
					if ( progress ) { progress.complete( msg ); } else { mbsCsvMessage( msg, 'success' ); }
					return;
				}
				// Existing column-mapping flow continues for single-tab CSV/XLSX imports.
								currentToken   = response.data.token;
					currentColumns = response.data.columns;
					if ( progress ) { progress.complete( __( 'File ready — map your columns below.', 'my-book-showroom' ) ); }
					showMappingUI( response.data.columns, response.data.preview );
				} else {
					var uploadErr = wp.i18n.sprintf( __( 'Upload failed: %s', 'my-book-showroom' ), response.data.message || __( 'Unknown error', 'my-book-showroom' ) );
					if ( progress ) { progress.failed( uploadErr ); } else { mbsCsvMessage( uploadErr, 'error' ); }
				}
			},
			error: function ( jqXHR, textStatus ) {
				var uploadAjaxErr = wp.i18n.sprintf( __( 'AJAX error during upload: %s', 'my-book-showroom' ), textStatus );
				if ( progress ) { progress.failed( uploadAjaxErr ); } else { mbsCsvMessage( uploadAjaxErr, 'error' ); }
			},
			complete: function () {
				$btn.prop( 'disabled', false ).text( originalText );
			}
		} );
	}

	/**
	 * Reset all shared state and remove any injected UI fragments.
	 * Called by the upload guard before beginning a fresh upload.
	 */
	function resetImportState() {
		currentToken        = null;
		currentColumns      = [];
		currentNonce        = null;
		mappingActive       = false;
		$( '#mbs-mapping-container' ).remove();
		$( '#mbs-result-container' ).remove();
		mbsCsvMessage( '', '' );
		if ( progress ) { progress.reset(); }
	}

	// ── Column mapper ─────────────────────────────────────────────────────────────────────

	/**
	 * Inject the column-mapping table after the active import area.
	 * Sets mappingActive = true; cleared by executeImport() on completion.
	 *
	 * @param {string[]} columns CSV column names from the server.
	 * @param {Array}    preview First-row preview data (reserved for future use).
	 */
	function showMappingUI( columns, preview ) {
		mappingActive = true;

		// Use server-localized list; fall back to a safe default if missing.
		var accepted = ( MBS_Settings.acceptedColumns && MBS_Settings.acceptedColumns.length )
			? MBS_Settings.acceptedColumns
			: [
				'title', 'teaser', 'publisher', 'pub_date', 'coming_soon',
				'isbn13', 'asin', 'format', 'price', 'look_inside_text',
				'author', 'series', 'series_number', 'page_length',
				'cover_image_url', 'cover_image_file'
			];

		var html = '<div id="mbs-mapping-container" class="mbs-settings-card" style="margin-top:16px;">';
		html += '<h4 style="margin:0 0 12px;">Map CSV Columns to Book Fields</h4>';
		html += '<table class="widefat striped">';
		html += '<thead><tr><th>CSV Column</th><th>Map to MBS Field</th></tr></thead>';
		html += '<tbody>';

		columns.forEach( function ( col ) {
			html += '<tr>';
			html += '<td><strong>' + escHtml( col ) + '</strong></td>';

			// Passthrough columns (prefix _) are auto-mapped server-side by
			// build_field_map(); no user selection is needed or meaningful.
			if ( col.charAt( 0 ) === '_' ) {
				html += '<td><em style="color:#888;font-style:italic;">— auto-mapped —</em></td>';
			} else {
				var options = '<option value="">— Skip column —</option>';
				accepted.forEach( function ( field ) {
					var sel = ( col.toLowerCase() === field ) ? ' selected' : '';
					options += '<option value="' + escAttr( field ) + '"' + sel + '>' + escHtml( field ) + '</option>';
				} );
				html += '<td><select class="mbs-column-map" data-csv-col="' + escAttr( col ) + '">' + options + '</select></td>';
			}

			html += '</tr>';
		} );

		html += '</tbody></table>';
		html += '<p style="margin-top:14px;">';
		html += '<button type="button" id="mbs-import-execute" class="button button-primary">' + escHtml( __( 'Import Books Now', 'my-book-showroom' ) ) + '</button>';
		html += '</p>';
		html += '</div>';

		$( currentAreaSelector ).after( html );

		$( '#mbs-mapping-container' ).on( 'click', '#mbs-import-execute', function ( e ) {
			e.preventDefault();
			executeImport();
		} );
	}

	// ── Import executor ───────────────────────────────────────────────────────────────────────

	/**
	 * Collect the column map from the mapping UI and POST to mbs_import_csv.
	 * On success: renders the result panel and triggers cover sideload.
	 * On failure: re-enables the button for retry.
	 */
	function executeImport() {
		var map = {};
		$( '.mbs-column-map' ).each( function () {
			var csvCol   = $( this ).data( 'csv-col' );
			var mbsField = $( this ).val();
			if ( mbsField ) {
				map[ csvCol ] = mbsField;
			}
		} );

		if ( Object.keys( map ).length === 0 ) {
			mbsCsvMessage( __( 'Please map at least one column before importing.', 'my-book-showroom' ), 'error' );
			return;
		}

		var $btn  = $( '#mbs-import-execute' );
		var nonce = currentNonce;

		$btn.prop( 'disabled', true ).text( __( 'Importing…', 'my-book-showroom' ) );
		$( '#mbs-result-container' ).remove();

		$.post( MBS_Settings.ajaxUrl, {
			action:            'mbs_import_csv',
			nonce:             nonce,
			token:             currentToken,
			column_map:        JSON.stringify( map ),
			force_insert_keys: JSON.stringify( [] )
		}, function ( response ) {
			if ( response.success ) {
				mappingActive = false;
				var impData     = response.data;
				var impInserted = impData.inserted || 0;
				var impSkipped  = impData.skipped  || 0;
				var impErrors   = ( impData.errors || [] ).length;
				var impSummary  = impInserted + ' ' + __( 'book(s) imported.', 'my-book-showroom' );
				if ( impSkipped ) { impSummary += ' ' + impSkipped + ' ' + __( 'skipped.', 'my-book-showroom' ); }
				if ( impErrors  ) { impSummary += ' ' + impErrors  + ' ' + __( 'error(s) — see details below.', 'my-book-showroom' ); }
				if ( progress ) { progress.complete( impSummary ); }
				renderResultPanel( response.data );
				$( '#mbs-mapping-container' ).remove();
				triggerCoverSideload( nonce );
				// Clear the file input so the filename no longer shows after import.
				$( '#mbs-csv-file' ).val( '' );
			} else {
				var impFailMsg = wp.i18n.sprintf( __( 'Import failed: %s', 'my-book-showroom' ), response.data.message || __( 'Unknown error', 'my-book-showroom' ) );
				if ( progress ) { progress.failed( impFailMsg ); } else { mbsCsvMessage( impFailMsg, 'error' ); }
				$btn.prop( 'disabled', false ).text( __( 'Import Books Now', 'my-book-showroom' ) );
			}
		} ).fail( function ( jqXHR, textStatus ) {
			var impAjaxErr = wp.i18n.sprintf( __( 'AJAX error during import: %s', 'my-book-showroom' ), textStatus );
			if ( progress ) { progress.failed( impAjaxErr ); } else { mbsCsvMessage( impAjaxErr, 'error' ); }
			$btn.prop( 'disabled', false ).text( __( 'Import Books Now', 'my-book-showroom' ) );
		} );
	}

	// ── Result renderer ───────────────────────────────────────────────────────────────────────

	/**
	 * Inject the post-import result panel after the active import area.
	 *
	 * Duplicates are listed as a group with a single "Force Insert All"
	 * batch button rather than individual per-row buttons. This prevents
	 * the second force-insert from failing because the PHP handler deletes
	 * the session transient after the first successful import call.
	 *
	 * @param {Object} data  Response data from mbs_import_csv.
	 */
	function renderResultPanel( data ) {
		var inserted   = data.inserted   || 0;
		var skipped    = data.skipped    || 0;
		var duplicates = data.duplicates || [];
		var errors     = data.errors     || [];
		var total      = inserted + skipped + errors.length;
		var nonce      = currentNonce;

		var html = '<div id="mbs-result-container" class="mbs-result-panel">';

		// Summary line.
		var summaryText;
		if ( inserted === total && total > 0 ) {
			summaryText = wp.i18n.sprintf(
				_n( 'All %d book imported successfully.', 'All %d books imported successfully.', total, 'my-book-showroom' ),
				total
			);
		} else if ( total === 0 ) {
			summaryText = __( 'No books were imported.', 'my-book-showroom' );
		} else {
			summaryText = wp.i18n.sprintf(
				_n( '%1$d of %2$d book imported successfully.', '%1$d of %2$d books imported successfully.', total, 'my-book-showroom' ),
				inserted, total
			);
		}
		html += '<p class="mbs-result-summary">' + escHtml( summaryText ) + '</p>';

		// Duplicate group — batch force-insert button carries all keys at once.
		// Individual per-row buttons are intentionally avoided: the PHP handler
		// deletes the upload session after the first successful call, so a
		// second per-row click always fails with "Upload session expired."
		if ( duplicates.length > 0 ) {
			var forceKeys = [];

			html += '<div class="mbs-result-duplicates">';
			html += '<p class="mbs-result-warning"><strong>' + escHtml( wp.i18n.sprintf(
				_n( 'Duplicate detected (%d):', 'Duplicates detected (%d):', duplicates.length, 'my-book-showroom' ),
				duplicates.length
			) ) + '</strong></p>';
			html += '<ul style="margin:4px 0 10px 20px; list-style:disc;">';

			duplicates.forEach( function ( dupe ) {
				var candidate = dupe.data || dupe;
				var title     = candidate.title || '(untitled)';
				var isbn      = candidate.isbn13 || '';
				var forceKey  = isbn ? 'isbn13:' + isbn : 'title:' + mbsSanitizeTitle( title );
				var label     = escHtml( title ) + ( isbn ? ' <em>(ISBN: ' + escHtml( isbn ) + ')</em>' : '' );

				forceKeys.push( forceKey );
				html += '<li>' + label + '</li>';
			} );

			html += '</ul>';
			html += '<button type="button" class="button button-secondary mbs-result-force-insert-all"';
			html += ' data-force-keys="' + escAttr( JSON.stringify( forceKeys ) ) + '"';
			html += ' data-nonce="' + escAttr( nonce ) + '">';
			html += escHtml( wp.i18n.sprintf(
				_n( 'Force Insert All %d Duplicate', 'Force Insert All %d Duplicates', duplicates.length, 'my-book-showroom' ),
				duplicates.length
			) );
			html += '</button>';
			html += '</div>';
		}

		// Error lines.
		errors.forEach( function ( msg ) {
			html += '<p class="mbs-result-error">' + escHtml( msg ) + '</p>';
		} );

		// Skips that are not accounted for by the duplicate list.
		var nakedSkips = skipped - duplicates.length;
		if ( nakedSkips > 0 ) {
			html += '<p class="mbs-result-warning">' +
				escHtml( wp.i18n.sprintf(
					_n( '%d row skipped (no title or invalid data).', '%d rows skipped (no title or invalid data).', nakedSkips, 'my-book-showroom' ),
					nakedSkips
				) ) +
				'</p>';
		}

		// Competitor fidelity notice — shown when at least one book was imported from a
		// competitor file whose export format cannot carry MBS's full per-format data.
		// To add a notice for a new competitor: set source='<slug>' on the upload
		// transient in PHP, pass it through the success response, then add an else-if
		// branch here. See DEVELOPER-REFERENCE.md § "Competitor-importer fidelity notice".
		if ( data.source === 'mybooktable' && inserted > 0 ) {
			html += '<div class="mbs-result-notice mbs-result-notice--mbt">';
			html += '<p><strong>' + escHtml( __( 'A note about your imported books', 'my-book-showroom' ) ) + '</strong></p>';
			html += '<p>' + __( 'My Book Showroom supports richer book data than MyBookTable exports — things like per-format pricing, page counts, reading samples, and detailed publisher information. We\'ve done our best to faithfully import everything MyBookTable provided, but we recommend reviewing each book to:', 'my-book-showroom' ) + '</p>';
			html += '<ul>';
			html += '<li>' + __( 'Confirm the <strong>format</strong> is correct (we inferred Paperback or eBook from the identifiers present, but you may want to add additional formats).', 'my-book-showroom' ) + '</li>';
			html += '<li>' + __( 'Add any <strong>per-format details</strong> — price, page count, sample text — that differ between your editions.', 'my-book-showroom' ) + '</li>';
			html += '<li>' + __( 'Verify your <strong>cover images</strong> have loaded correctly (remote images are queued and sideloaded in the background).', 'my-book-showroom' ) + '</li>';
			html += '<li>' + __( 'Review your <strong>buy links</strong> to make sure every retailer and store is accounted for.', 'my-book-showroom' ) + '</li>';
			html += '</ul>';
			html += '<p>' + __( 'You\'re in good hands — it only takes a few minutes per book to get the most out of what My Book Showroom can do for you.', 'my-book-showroom' ) + '</p>';
			html += '</div>';
		}

		html += '</div>';

		$( currentAreaSelector ).after( html );

		// Batch force-insert handler — one POST for all queued keys.
		$( '#mbs-result-container' ).on( 'click', '.mbs-result-force-insert-all', function () {
			executeBatchForceInsert( $( this ) );
		} );
	}

	/**
	 * Send all accumulated force-insert keys in a single POST to mbs_import_csv.
	 * Replaces the duplicate group with a success or error message in place.
	 *
	 * @param {jQuery} $btn The "Force Insert All" button element.
	 */
	function executeBatchForceInsert( $btn ) {
		var forceKeys = [];
		try {
			forceKeys = JSON.parse( $btn.data( 'force-keys' ) || '[]' );
		} catch ( e ) {
			mbsCsvMessage( 'Could not parse force-insert keys.', 'error' );
			return;
		}

		if ( ! forceKeys.length ) {
			return;
		}

		var nonce     = $btn.data( 'nonce' );
		var $group    = $btn.closest( '.mbs-result-duplicates' );
		var bookCount = forceKeys.length;

		$btn.prop( 'disabled', true ).text( __( 'Inserting…', 'my-book-showroom' ) );

		$.post( MBS_Settings.ajaxUrl, {
			action:            'mbs_import_csv',
			nonce:             nonce,
			token:             currentToken,
			column_map:        JSON.stringify( {} ),
			force_insert_keys: JSON.stringify( forceKeys )
		}, function ( response ) {
			if ( response.success && response.data.inserted > 0 ) {
				$group.replaceWith(
					'<p class="mbs-result-warning" style="color:#2e7d32;">' +
					'✓ ' + escHtml( wp.i18n.sprintf(
						_n( '%1$d of %2$d duplicate force inserted.', '%1$d of %2$d duplicates force inserted.', bookCount, 'my-book-showroom' ),
						response.data.inserted, bookCount
					) ) +
					'</p>'
				);
				triggerCoverSideload( nonce );
			} else {
				var msg = ( response.data && response.data.message )
					? response.data.message
					: __( 'Force insert failed.', 'my-book-showroom' );
				$btn.prop( 'disabled', false ).text(
					wp.i18n.sprintf( __( 'Retry Force Insert (%d)', 'my-book-showroom' ), bookCount )
				);
				$btn.after( ' <em>' + escHtml( msg ) + '</em>' );
			}
		} ).fail( function () {
			$btn.prop( 'disabled', false ).text(
				wp.i18n.sprintf( __( 'Retry Force Insert (%d)', 'my-book-showroom' ), bookCount )
			);
		} );
	}

	// ── Cover sideload ────────────────────────────────────────────────────────────────────────

	/**
	 * Fire the mbs_sideload_covers AJAX action and append cover results to the
	 * existing result panel. Polls until response.data.remaining reaches zero.
	 *
	 * @param {string} nonce  mbs_import_export nonce value.
	 */
	function triggerCoverSideload( nonce ) {
		var $panel = $( '#mbs-result-container' );
		if ( ! $panel.length ) {
			return;
		}

		$panel.append(
			'<p id="mbs-cover-status" style="margin:10px 0 0; color:#50575e; font-style:italic;">' +
			MBS_AdminUtils.escHtml( __( 'Processing cover images…', 'my-book-showroom' ) ) +
			'</p>'
		);

		( function poll() {
			$.post( MBS_Settings.ajaxUrl, {
				action: 'mbs_sideload_covers',
				nonce:  nonce
			}, function ( response ) {
				var $status = $( '#mbs-cover-status' );

				if ( ! response.success ) {
					$status.text( wp.i18n.sprintf( __( 'Cover processing failed: %s', 'my-book-showroom' ), ( response.data && response.data.message ) || __( 'Unknown error', 'my-book-showroom' ) ) );
					return;
				}

				var d = response.data;

				// Append per-book warnings and errors from this pass.
				d.warnings.forEach( function ( msg ) {
					$panel.append( '<p class="mbs-result-warning">' + escHtml( msg ) + '</p>' );
				} );
				d.errors.forEach( function ( msg ) {
					$panel.append( '<p class="mbs-result-error">' + escHtml( msg ) + '</p>' );
				} );

				if ( d.remaining > 0 ) {
					$status.text( wp.i18n.sprintf(
						__( 'Processing covers… %d remaining.', 'my-book-showroom' ),
						d.remaining
					) );
					setTimeout( poll, 700 );
				} else if ( d.processed === 0 ) {
					// Nothing to process — remove the status line silently.
					$status.remove();
				} else {
					// All done — show a brief cover summary.
					var summary = wp.i18n.sprintf(
						_n( '%d cover image imported.', '%d cover images imported.', d.succeeded, 'my-book-showroom' ),
						d.succeeded
					);
					if ( d.warnings.length + d.errors.length > 0 ) {
						summary += ' ' + __( 'See notes above.', 'my-book-showroom' );
					}
					$status.removeAttr( 'style' ).text( summary );
				}
			} ).fail( function () {
				$( '#mbs-cover-status' ).text( __( 'Cover processing unavailable — images can be added manually.', 'my-book-showroom' ) );
			} );
		} )();
	}

	// ── Helpers ─────────────────────────────────────────────────────────────────────────────

	/**
	 * Show or clear the status message above the active import area.
	 *
	 * @param {string} text  Message text. Empty string clears the message.
	 * @param {string} type  'error' | 'success' | 'info'
	 */
	function mbsCsvMessage( text, type ) {
		var $msg = $( '#mbs-csv-message' );
		if ( ! $msg.length ) {
			$( currentAreaSelector ).after(
				'<div id="mbs-csv-message" style="margin-top:8px; display:none;"></div>'
			);
			$msg = $( '#mbs-csv-message' );
		}
		if ( ! text ) {
			$msg.hide().empty();
			return;
		}
		var cls = { error: 'notice-error', success: 'notice-success', info: 'notice-info' }[ type ] || 'notice-info';
		$msg
			.removeClass( 'notice-error notice-success notice-info' )
			.addClass( 'notice ' + cls )
			.html( '<p>' + escHtml( text ) + '</p>' )
			.show();
	}

	/* escHtml / escAttr — use shared MBS_AdminUtils (mbs-admin-utils.js) */
	var escHtml = MBS_AdminUtils.escHtml;
	var escAttr = MBS_AdminUtils.escAttr;

	/**
	 * Normalise a title string into a slug-style key for force-insert matching.
	 * Mirrors the PHP-side key format expected by MBS_Parent_Importer_CSV.
	 *
	 * @param {string} str Raw title string.
	 * @return {string}
	 */
	function mbsSanitizeTitle( str ) {
		return String( str )
			.toLowerCase()
			.replace( /[^a-z0-9\s-]/g, '' )
			.trim()
			.replace( /\s+/g, '-' )
			.replace( /-+/g, '-' );
	}

} )( jQuery );
