// Bank Reconciliation Tool extensions for jey_erp.
//
// All wiring happens via a one-time DataTableManager prototype patch (on the
// form's refresh):
//   - get_dt_columns / format_row : adds a "Bank Transaction" column (BT name + link)
//   - get_datatable               : creates the table WITH a checkbox column and
//                                   injects the "Create & Reconcile" toolbar
//                                   (done here so there is no destroy/rebuild flicker)
//
// "Create & Reconcile" maps the selected Bank Transactions to Payment Entry /
// Journal Entry using a chosen mapping source: a Bank Statement Importer record
// (jey_erp) or Kapital Bank Settings (kapital_bank). The default source comes
// from the Bank Account's hidden bank_integration field and is written back on
// submit. The dialog / mapping logic is a port of kapital_bank's BRT extension.

frappe.ui.form.on("Bank Reconciliation Tool", {
	refresh(frm) {
		frm.$wrapper.closest(".page-container").css("--page-max-width", "none");

		frappe.require("bank-reconciliation-tool.bundle.js", function () {
			const DTM = erpnext.accounts.bank_reconciliation.DataTableManager;
			if (!DTM || DTM.prototype._jey_erp_patched) return;
			const proto = DTM.prototype;

			// --- "Bank Transaction" column ---
			const orig_get_dt_columns = proto.get_dt_columns;
			proto.get_dt_columns = function () {
				orig_get_dt_columns.call(this);
				this.columns.splice(this.columns.length - 1, 0, {
					name: __("Bank Transaction"),
					editable: false,
					width: 180,
					format: (value) =>
						value
							? `<a href="/app/bank-transaction/${encodeURIComponent(value)}" target="_blank">${value}</a>`
							: "",
				});
			};

			const orig_format_row = proto.format_row;
			proto.format_row = function (row) {
				const result = orig_format_row.call(this, row);
				result.splice(result.length - 1, 0, row["name"]);
				return result;
			};

			// --- checkbox column + Create & Reconcile toolbar ---
			// Replace get_datatable wholesale so the table is built with
			// checkboxColumn:true from the start (no visible rebuild).
			proto.get_datatable = function () {
				this.datatable = new frappe.DataTable(this.$reconciliation_tool_dt.get(0), {
					columns: this.columns,
					data: this.transactions,
					dynamicRowHeight: true,
					checkboxColumn: true,
					inlineFilters: true,
				});
				$(`.${this.datatable.style.scopeClass} .dt-scrollable`).css("max-height", "calc(100vh - 400px)");

				if (this.transactions.length > 0) {
					this.$reconciliation_tool_dt.show();
					this.$no_bank_transactions.hide();
				} else {
					this.$reconciliation_tool_dt.hide();
					this.$no_bank_transactions.show();
				}

				this._bi_scopeClass = this.datatable.style.scopeClass;
				BIBRT.injectToolbar(this);
			};

			proto._jey_erp_patched = true;
		});
	},
});

const BIBRT = {
	injectToolbar: function (dtm) {
		$("#bi-brt-toolbar").remove();

		const $toolbar = $(`
			<div id="bi-brt-toolbar"
				style="display:none; align-items:center; gap:10px; margin-bottom:8px;
				       padding:8px 12px; background:var(--fg-color);
				       border:1px solid var(--border-color); border-radius:6px;">
				<span class="bi-brt-selected-count text-muted"></span>
				<button class="btn btn-primary btn-sm bi-brt-create-btn">
					${__("Create & Reconcile")}
				</button>
			</div>
		`);
		dtm.$reconciliation_tool_dt.before($toolbar);

		const scopeClass = dtm.datatable.style.scopeClass;
		$(document).off("click.bi_brt").on(
			"click.bi_brt",
			`.${scopeClass} input[type="checkbox"]`,
			function () {
				setTimeout(() => BIBRT.updateToolbar(dtm, $toolbar), 30);
			}
		);

		$toolbar.find(".bi-brt-create-btn").on("click", function () {
			BIBRT.showMappingDialog(dtm);
		});
	},

	updateToolbar: function (dtm, $toolbar) {
		const checked = dtm.datatable.rowmanager.getCheckedRows();
		if (checked && checked.length > 0) {
			$toolbar.css("display", "flex");
			$toolbar.find(".bi-brt-selected-count").text(__("{0} row(s) selected", [checked.length]));
		} else {
			$toolbar.hide();
		}
	},

	showMappingDialog: function (dtm) {
		const checkedIndices = dtm.datatable.rowmanager.getCheckedRows();
		if (!checkedIndices || checkedIndices.length === 0) {
			frappe.msgprint({ title: __("Warning"), indicator: "orange", message: __("No rows selected.") });
			return;
		}

		const bankAccount = dtm.bank_account;
		if (!bankAccount) {
			frappe.msgprint({ title: __("Bank Account Required"), indicator: "orange",
				message: __("Pick a Bank Account in the form first.") });
			return;
		}

		// dtm.transactions rows are arrays. With the jey_erp prototype patch active:
		// [0]date [1]party_type [2]party(HTML) [3]description [4]deposit [5]withdrawal
		// [6]unallocated [7]reference_number [8]bank_transaction_name(string) [9]actions(button HTML)
		const txns = [];
		let skippedCount = 0;
		checkedIndices.forEach(function (rowIndex) {
			const row = dtm.transactions[parseInt(rowIndex, 10)];
			if (!row) return;
			const description = (row[3] || "").trim();
			if (!description) { skippedCount++; return; }
			const partyType = (row[1] || "").trim();
			const partyName = $("<div>").html(row[2] || "").text().trim();
			const deposit = parseFloat(row[4]) || 0;
			const withdrawal = parseFloat(row[5]) || 0;

			let btName = "";
			if (typeof row[8] === "string" && row[8] && row[8].indexOf("<") === -1) {
				btName = row[8].trim();
			}
			if (!btName) btName = $(row[9] || row[8] || "").data("name") || "";

			txns.push({
				purpose: description,
				drcr: withdrawal > 0 ? "D" : "C",
				party_type: partyType,
				party: partyName,
				amount: deposit || withdrawal,
				date: (row[0] || "").trim(),
				reference_number: (row[7] || "").trim(),
				bank_transaction_name: btName,
			});
		});

		if (txns.length === 0) {
			frappe.msgprint({ title: __("No Transactions"), indicator: "orange",
				message: __("All selected rows have no description text.") });
			return;
		}
		if (txns.some(t => !t.bank_transaction_name)) {
			frappe.msgprint({ title: __("Error"), indicator: "red",
				message: __("Could not resolve Bank Transaction names from selection. Try reloading the page.") });
			return;
		}

		Promise.all([
			frappe.db.get_list("Bank Statement Importer", { fields: ["name", "bank_name"], limit: 200 }),
			frappe.db.get_value("Bank Account", bankAccount, ["bank_integration_type", "bank_integration"]),
			frappe.db.get_list("DocType", { filters: { name: "Kapital Bank Settings" }, fields: ["name"], limit: 1 }),
		]).then(([integrations, baResp, kbList]) => {
			const baFields = (baResp && baResp.message) || {};
			const kbInstalled = kbList && kbList.length > 0;

			const sourceMap = {};
			(integrations || []).forEach(i => {
				sourceMap["Bank Statement Importer::" + i.name] = __("Bank Statement Importer: {0}", [i.bank_name || i.name]);
			});
			if (kbInstalled) {
				sourceMap["Kapital Bank Settings::Kapital Bank Settings"] = __("Kapital Bank Settings");
			}

			const sourceValues = Object.keys(sourceMap);
			if (sourceValues.length === 0) {
				frappe.msgprint({ title: __("No Mapping Source"), indicator: "orange",
					message: __("Create at least one Bank Statement Importer record first.") });
				return;
			}

			let defaultSource = "";
			if (baFields.bank_integration_type && baFields.bank_integration) {
				const candidate = baFields.bank_integration_type + "::" + baFields.bank_integration;
				if (sourceMap[candidate]) defaultSource = candidate;
			}

			BIBRT._renderDialog(dtm, bankAccount, txns, skippedCount, sourceMap, sourceValues, defaultSource);
		});
	},

	_renderDialog: function (dtm, bankAccount, txns, skippedCount, sourceMap, sourceValues, defaultSource) {
		const purposeMap = new Map();
		txns.forEach(function (txn) {
			const key = txn.purpose + "|" + txn.drcr;
			if (purposeMap.has(key)) {
				purposeMap.get(key).count++;
			} else {
				purposeMap.set(key, {
					purpose: txn.purpose,
					payment_type: txn.drcr === "D" ? "Pay" : "Receive",
					party: txn.party,
					count: 1,
				});
			}
		});

		let previewHtml = '<div style="max-height:240px; overflow-y:auto; margin-bottom:12px;">' +
			'<table class="table table-bordered" style="width:100%;">' +
			"<thead><tr>" +
			"<th>" + __("Description") + "</th>" +
			'<th style="width:90px;">' + __("Type") + "</th>" +
			'<th style="width:140px;">' + __("Party") + "</th>" +
			'<th style="width:60px; text-align:center;">' + __("Rows") + "</th>" +
			"</tr></thead><tbody>";
		purposeMap.forEach(function (e) {
			const badge = e.payment_type === "Pay"
				? '<span class="label label-danger">Pay</span>'
				: '<span class="label label-success">Receive</span>';
			previewHtml += "<tr>" +
				'<td style="word-break:break-word;font-size:0.9em;">' + frappe.utils.escape_html(e.purpose) + "</td>" +
				"<td>" + badge + "</td>" +
				"<td>" + (e.party ? frappe.utils.escape_html(e.party) : '<span class="text-muted">—</span>') + "</td>" +
				'<td style="text-align:center;">' + e.count + "</td></tr>";
		});
		previewHtml += "</tbody></table></div>";
		if (skippedCount > 0) {
			previewHtml += '<div class="alert alert-warning">' +
				__("{0} row(s) skipped — no description text.", [skippedCount]) + "</div>";
		}

		const d = new frappe.ui.Dialog({
			title: __("Create & Reconcile") + " (" + purposeMap.size + ")",
			size: "large",
			fields: [
				{
					fieldname: "source",
					fieldtype: "Select",
					label: __("Mapping Source"),
					options: sourceValues.join("\n"),
					default: defaultSource || sourceValues[0],
					reqd: 1,
					description: __("Which Bank Statement Importer / Kapital Bank Settings provides the mappings."),
				},
				{
					fieldname: "mode",
					fieldtype: "Select",
					label: __("Action"),
					options: "Both\nMappings Only\nDocuments & Reconcile",
					default: "Both",
					reqd: 1,
					onchange: function () { BIBRT._applyModeVisibility(d); },
				},
				{ fieldname: "preview_html", fieldtype: "HTML", options: previewHtml },
				{ fieldname: "sec_options", fieldtype: "Section Break", label: __("Options") },
				{
					fieldname: "document_type",
					fieldtype: "Select",
					label: __("Document Type"),
					options: "Payment Entry\nJournal Entry",
					default: "Payment Entry",
				},
				{
					fieldname: "use_original_purpose",
					fieldtype: "Check",
					label: __("Use original description"),
					onchange: function () {
						const on = d.get_value("use_original_purpose");
						d.set_df_property("custom_purpose", "hidden", on ? 1 : 0);
						d.get_field("custom_purpose").refresh();
					},
				},
				{
					fieldname: "custom_purpose",
					fieldtype: "Data",
					label: __("Custom Purpose Keyword"),
					hidden: 0,
					description: __("Leave empty to map by party only. All rows will use this keyword instead of their own description."),
				},
				{ fieldname: "sec_accounts", fieldtype: "Section Break", label: __("GL Accounts") },
				{ fieldname: "paid_from", fieldtype: "Link", options: "Account", label: __("Paid From (Account)") },
				{ fieldname: "col_break", fieldtype: "Column Break" },
				{ fieldname: "paid_to", fieldtype: "Link", options: "Account", label: __("Paid To (Account)") },
			],
			primary_action_label: __("Create & Reconcile"),
			primary_action: function () {
				const values = d.get_values(true);
				if (!values) return;
				const isDR = values.mode === "Documents & Reconcile";
				if (!isDR) {
					const missing = [];
					if (!values.paid_from) missing.push(__("Paid From (Account)"));
					if (!values.paid_to) missing.push(__("Paid To (Account)"));
					if (missing.length) {
						frappe.msgprint({ title: __("Missing Values"), indicator: "red",
							message: __("Following fields are required:") + "<br>" + missing.join("<br>") });
						return;
					}
				}

				let finalTxns = txns.slice();
				if (!isDR && !values.use_original_purpose) {
					const cp = (values.custom_purpose || "").trim();
					if (cp) {
						finalTxns = txns.map(t => Object.assign({}, t, { purpose: cp }));
					} else {
						finalTxns = txns.map(t => Object.assign({}, t, { purpose: "" }));
						if (finalTxns.every(t => !t.party)) {
							frappe.msgprint({ title: __("Validation Error"), indicator: "red",
								message: __("Either a purpose keyword or a party is required.") });
							return;
						}
					}
				}

				const [sourceType, sourceName] = values.source.split("::");
				d.disable_primary_action();

				frappe.call({
					method: "jey_erp.bank_integration.import_api.update_bank_account_bi_default",
					args: { bank_account: bankAccount, bi_type: sourceType, bi_name: sourceName },
					always: function () {
						BIBRT._submit(d, finalTxns, sourceType, sourceName,
							values.paid_from, values.paid_to, values.document_type, values.mode);
					},
				});
			},
		});

		d._bi_scopeClass = dtm._bi_scopeClass;
		d.show();
		BIBRT._applyModeVisibility(d);

		const $fi = $(`.${dtm._bi_scopeClass} .dt-row-filter input.dt-filter[data-name="Description"]`);
		const filterVal = ($fi.val() || "").trim();
		if (filterVal) d.set_value("custom_purpose", filterVal);
	},

	_applyModeVisibility: function (d) {
		const isDR = d.get_value("mode") === "Documents & Reconcile";
		// "Use original description" also hides custom_purpose; fold that into the
		// per-field decision so toggling Action modes never reveals a field the
		// checkbox had hidden.
		const useOriginal = !!d.get_value("use_original_purpose");
		const fields = ["preview_html", "sec_options", "document_type", "use_original_purpose",
			"custom_purpose", "sec_accounts", "paid_from", "col_break", "paid_to"];
		fields.forEach(function (fn) {
			let hidden = isDR ? 1 : 0;
			if (fn === "custom_purpose" && !isDR && useOriginal) hidden = 1;
			d.set_df_property(fn, "hidden", hidden);
			const field = d.get_field(fn);
			if (field && typeof field.refresh === "function") field.refresh();
		});
	},

	_submit: function (dialog, txns, sourceType, sourceName, paid_from, paid_to, document_type, mode) {
		const isKB = sourceType === "Kapital Bank Settings";
		const method = isKB
			? "kapital_bank.mapping.create_purpose_mappings"
			: "jey_erp.bank_integration.create_reconcile.create_purpose_mappings";
		const args = isKB
			? {
				transactions: JSON.stringify(txns),
				paid_from: paid_from,
				paid_to: paid_to,
				document_type: document_type || "Payment Entry",
				mode: mode || "Both",
			}
			: {
				transactions: JSON.stringify(txns),
				bank_integration: sourceName,
				paid_from: paid_from,
				paid_to: paid_to,
				document_type: document_type || "Payment Entry",
				mode: mode || "Both",
			};

		frappe.call({
			method: method,
			args: args,
			callback: function (r) {
				dialog.enable_primary_action();
				if (!r.message || !r.message.success) {
					frappe.msgprint({ title: __("Error"), indicator: "red",
						message: r.message ? r.message.message : __("Unknown error") });
					return;
				}
				dialog.hide();
				const res = r.message;
				const parts = [];
				if (res.created_mappings) parts.push(__("Mappings created: {0}", [res.created_mappings]));
				if (res.already_mapped) parts.push(__("Already mapped: {0}", [res.already_mapped]));
				if (res.skipped_no_data) parts.push(__("Skipped (no purpose): {0}", [res.skipped_no_data]));
				if (res.created_docs) parts.push(__("Documents created: {0}", [res.created_docs]));
				if (res.reconciled) parts.push(__("Reconciled: {0}", [res.reconciled]));
				if (res.errors && res.errors.length) parts.push(__("Errors: {0}", [res.errors.length]));
				frappe.show_alert({
					message: parts.join(" &bull; ") || __("No changes"),
					indicator: (res.created_mappings > 0 || res.created_docs > 0) ? "green" : "blue",
				}, 6);
				if (cur_frm && cur_frm.doctype === "Bank Reconciliation Tool") cur_frm.refresh();
				if (res.errors && res.errors.length) BIBRT.showErrors(res.errors);
			},
			error: function () {
				dialog.enable_primary_action();
				frappe.msgprint({ title: __("Error"), indicator: "red",
					message: __("Network error creating mappings") });
			},
		});
	},

	showErrors: function (errors) {
		let html = '<div style="max-height:500px; overflow-y:auto;">';
		errors.forEach(function (err) {
			const ref = err.reference_number || err.bank_transaction || __("Unknown");
			html += '<div style="margin-bottom:14px; padding:12px; border:1px solid var(--border-color); border-radius:6px; background:var(--bg-color);">';
			html += '<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:6px; padding-bottom:6px; border-bottom:1px solid var(--border-color);">';
			html += "<strong>" + frappe.utils.escape_html(ref) + "</strong>";
			html += '<span class="label label-danger">' + __("Error") + "</span>";
			html += "</div>";
			html += '<div style="color:var(--text-color);">' + frappe.utils.escape_html(err.message || __("Unknown error")) + "</div>";
			html += "</div>";
		});
		html += "</div>";

		const d = new frappe.ui.Dialog({
			title: __("Errors ({0})", [errors.length]),
			size: "large",
			fields: [{ fieldname: "errors_html", fieldtype: "HTML", options: html }],
			primary_action_label: __("Close"),
			primary_action: function () { d.hide(); },
		});
		d.show();
	},
};

window.BIBRT = BIBRT;
