// ============================================================
// SilentSniper – eBay/Amazon dropshipping Chrome-extension
// Deobfuscated & cleaned (membership / credential references
// that were embedded in the original have been removed)
// ============================================================


// ─────────────────────────────────────────────
// Blacklist helpers
// ─────────────────────────────────────────────

async function updateBlacklistFromServer() {
    console.log('updateBlacklistFromServer');
    try {
        const { domain } = await chrome.storage.local.get('domain');
        const { blacklist_custom_enabled, blacklist_custom } =
            await chrome.storage.local.get(['blacklist_custom_enabled', 'blacklist_custom']);

        const isGermany = domain === 'de';
        let autoList = [];
        let lastUpdated = new Date().toISOString();

        if (isGermany) {
            const url = 'https://operationapp-djybcnnsgq-uc.a.run.app/api/v1/germanBlackList/status/approved';
            const res = await fetch(url);
            const json = await res.json();
            autoList = Array.isArray(json)
                ? json.map(item => String(item.title || '').toLowerCase().trim()).filter(Boolean)
                : [];
            lastUpdated = new Date().toISOString();
        } else {
            autoList = [];
            lastUpdated = new Date().toISOString();
        }

        await chrome.storage.local.set({ blacklist_auto: autoList, blacklist_last_updated: lastUpdated });

        if (blacklist_custom_enabled) {
            const customList = Array.isArray(blacklist_custom) ? blacklist_custom : [];
            await chrome.storage.local.set({ blacklist: customList });
        } else {
            await chrome.storage.local.set({ blacklist: autoList });
        }

        console.log(
            `✅ Blacklist (auto ${isGermany ? 'DE' : 'non-DE'}) updated at ${lastUpdated} with ${autoList.length} words`
        );
        return { blacklist: autoList, lastUpdated };
    } catch (err) {
        console.error('❌ Failed to update blacklist:', err);
        return null;
    }
}


// ─────────────────────────────────────────────
// OpenAI structured-output library
// ─────────────────────────────────────────────

(function () {
    let defaultModel = 'gpt-5-nano';
    let structuredEndpoint = 'https://us-central1-silentsnipe-ce4ca.cloudfunctions.net/structured_output';
    let openaiKeyOverride = null;

    // Validate the fields schema object
    function validateFields(fields) {
        if (!fields || typeof fields !== 'object') throw new Error('fields must be an object');
        const supportedTypes = { string: true, number: true, integer: true, boolean: true };
        for (const key in fields) {
            if (!Object.prototype.hasOwnProperty.call(fields, key)) continue;
            const type = fields[key];
            if (typeof type !== 'string') throw new Error(`Type for '${key}' must be a string`);
            if (type.indexOf('array[') === 0 && type.slice(-1) === ']') {
                const innerType = type.slice(6, -1).trim();
                if (!supportedTypes[innerType])
                    throw new Error(`Unsupported array type for '${key}': ${innerType}`);
            } else {
                if (!supportedTypes[type]) throw new Error(`Unsupported type for '${key}': ${type}`);
            }
        }
    }

    // Map JS type names to the API's short names
    function mapFields(fields) {
        const mapped = {};
        for (const key in fields) {
            if (!Object.prototype.hasOwnProperty.call(fields, key)) continue;
            const type = fields[key];
            if (type.indexOf('array[') === 0 && type.slice(-1) === ']') {
                const innerType = type.slice(6, -1).trim();
                const shortType =
                    innerType === 'string' ? 'str' :
                        innerType === 'number' ? 'float' :
                            innerType === 'integer' ? 'int' :
                                innerType === 'boolean' ? 'bool' : null;
                if (!shortType) throw new Error(`Unsupported inner array type for '${key}': ${innerType}`);
                mapped[key] = `list[${shortType}]`;
            } else {
                const shortType =
                    type === 'string' ? 'str' :
                        type === 'number' ? 'float' :
                            type === 'integer' ? 'int' :
                                type === 'boolean' ? 'bool' : null;
                if (!shortType) throw new Error(`Unsupported type for '${key}': ${type}`);
                mapped[key] = shortType;
            }
        }
        return mapped;
    }

    // Core structured-JSON request
    async function requestStructuredJson(options) {
        return new Promise(async resolve => {
            try {
                if (!options || !options.system || !options.user) {
                    resolve({ ok: false, error: 'system and user are required' });
                    return;
                }

                validateFields(options.fields);

                const modelDefinition = mapFields(options.fields);
                const payload = {
                    system_message: options.system,
                    user_message: options.user,
                    model_definition: modelDefinition,
                    openai_model: options.model || defaultModel,
                };

                const keyToUse = options.openaiKey || openaiKeyOverride;
                if (keyToUse) {
                    payload.openai_key = keyToUse;
                } else {
                    const email = await new Promise((res, rej) => {
                        chrome.storage.local.get(['email'], result => {
                            if (chrome.runtime.lastError) rej(new Error('Could not read email from storage'));
                            else res(result && result.email ? result.email : null);
                        });
                    });
                    if (!email) { resolve({ ok: false, error: 'missing email in storage' }); return; }
                    payload.email = email;
                }

                // Send through background script to avoid CORS issues
                const response = await new Promise((res, rej) => {
                    chrome.runtime.sendMessage(
                        { type: 'fetchData', url: structuredEndpoint, data: payload },
                        reply => {
                            if (reply) {
                                if (reply.error) rej(new Error(reply.error));
                                else res(reply.data);
                            } else rej(new Error('No background response'));
                        }
                    );
                });

                if (response && response.success) {
                    resolve({ ok: true, data: response.result });
                } else {
                    resolve({ ok: false, error: response?.errorMessage || 'structured output failed' });
                }
            } catch (err) {
                resolve({ ok: false, error: err?.message || String(err) });
            }
        });
    }

    // Convenience wrapper – request a single named field
    async function requestStructuredField(options) {
        return new Promise(async resolve => {
            try {
                if (!options) { resolve({ ok: false, error: 'options are required' }); return; }

                const name = typeof options.name === 'string' ? options.name.trim() : '';
                const type = typeof options.type === 'string' ? options.type.trim() : '';

                if (!name || !type) { resolve({ ok: false, error: 'name and type are required' }); return; }

                const fields = { [name]: type };
                const result = await requestStructuredJson({
                    system: options.system,
                    user: options.user,
                    fields,
                    model: options.model,
                    openaiKey: options.openaiKey,
                });

                if (!result.ok) { resolve(result); return; }

                const value = result.data ? result.data[name] : undefined;
                if (value === undefined) {
                    resolve({ ok: false, error: `field missing in response: ${name}` });
                    return;
                }
                resolve({ ok: true, data: value });
            } catch (err) {
                resolve({ ok: false, error: err?.message || String(err) });
            }
        });
    }

    // Public API
    window.openaiLibrary = {
        setDefaultModel(model) { defaultModel = model; return window.openaiLibrary; },
        setStructuredEndpoint(url) { structuredEndpoint = url; return window.openaiLibrary; },
        useOpenAIKey(key) { openaiKeyOverride = key || null; return window.openaiLibrary; },
        requestStructuredJson,
        requestStructuredField,
    };
})();


// ─────────────────────────────────────────────
// eBay mesh order-details helpers
// ─────────────────────────────────────────────

console.log('ebay/mesh_order_details/functions.js');

let autoOrderButtonQueue; // previously _0x210707

// Returns the item-ID element
function getItemIdElement() {
    return document.querySelector('.lineItemCardInfo__itemId.spaceTop');
}

// Create a "Get SKU" button when the SKU isn't already resolved
async function createSkuElementButton() {
    console.log('createSkuElementButton');
    const sku = await getSku();
    console.log('skuTest', sku);

    if (sku && sku.endsWith('==')) {
        console.log('No need to create SKU button. SKU ends with "==".');
        return;
    }

    let container = document.querySelector('.lineItemCardInfo__content .details');
    if (!container) container = document.body;

    const btn = document.createElement('button');
    btn.textContent = 'Get SKU';
    container.insertBefore(btn, container.children[1]);

    btn.addEventListener('click', async e => {
        e.preventDefault();
        const wrapper = document.createElement('div');
        wrapper.classList.add('lineItemCardInfo__sku', 'spaceTop');

        const labelSpan = document.createElement('span');
        labelSpan.classList.add('sh-secondary');
        labelSpan.textContent = 'Custom label (SKU): ';

        const valueSpan = document.createElement('span');
        valueSpan.classList.add('sh-secondary');

        const itemNumber = getItemNumber();
        const resolvedSku = await getSkuFromDescription(itemNumber);
        valueSpan.textContent = resolvedSku;

        wrapper.appendChild(labelSpan);
        wrapper.appendChild(valueSpan);
        container.replaceChild(wrapper, btn);
    });
}

// Build the "Copy" (Amazon link) button
async function createCopyLinkButton() {
    const btn = document.createElement('button');
    btn.innerText = 'Copy';
    btn.classList.add('amazon-copy-link-button');

    btn.addEventListener('click', async () => {
        const sku = await getSku();
        if (!sku) return null;
        console.log('sku', sku);
        const link = await buildAmazonLink(sku);
        navigator.clipboard.writeText(link);
        btn.classList.add('amazon-copy-link-button-clicked');
        btn.innerText = 'Copied';
    });

    return btn;
}

// Copy order details to clipboard (or virtual clipboard)
async function copyOrderDetails(useVirtualClipboard = false, clipKey) {
    console.log('copyOrderDetails');
    const orderDetails = await getOrderDetails();
    console.log('orderDetails', orderDetails);
    await chrome.storage.local.set({ orderDetails });

    const json = JSON.stringify(orderDetails);
    console.log('stringified orderDetails', json);

    if (useVirtualClipboard) {
        const data = { clipKey, content: JSON.stringify(orderDetails) };
        console.log('data', data);
        try {
            console.log('making request');
            const res = await fetch(
                'https://us-central1-silentsnipe-ce4ca.cloudfunctions.net/app/api/v1/virtualClipboard/',
                { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }
            );
            console.log('response', res);
            if (!res.ok) throw new Error('Failed to save order details');
            const result = await res.json();
            console.log('Order details saved successfully:', result);
            return result;
        } catch (err) {
            console.error('Error saving order details:', err);
            throw err;
        }
    } else {
        navigator.clipboard.writeText(json);
    }
}

// Create the copy-address checkbox + label
async function createCopyAddressCheckbox() {
    let { shouldCopyAddress } = await chrome.storage.local.get('shouldCopyAddress');
    if (shouldCopyAddress === undefined) {
        shouldCopyAddress = true;
        await chrome.storage.local.set({ shouldCopyAddress });
    }

    const checkbox = document.createElement('input');
    checkbox.setAttribute('type', 'checkbox');
    checkbox.classList.add('amazon-copy-address-field');
    checkbox.setAttribute('id', 'amazon-copy-address-field');

    const label = document.createElement('label');
    label.innerText = 'Copy Address';
    label.setAttribute('for', 'amazon-copy-address-field');
    label.classList.add('amazon-copy-address-label');

    const div = document.createElement('div');
    div.id = 'amazon-copy-address-div';
    div.appendChild(label);
    div.appendChild(checkbox);

    checkbox.checked = shouldCopyAddress;
    checkbox.addEventListener('change', async () => {
        await chrome.storage.local.set({ shouldCopyAddress: checkbox.checked });
    });

    return div;
}

// Build the quantity-increase widget
function createQuantityWidget() {
    const container = document.createElement('div');
    container.classList.add('amazon-quantity-container');

    const label = document.createElement('label');
    label.innerText = 'Increase Quantity';
    label.classList.add('amazon-quantity-label');
    container.appendChild(label);

    const btn = document.createElement('button');
    btn.innerText = 'Increase Quantity';
    btn.classList.add('amazon-update-quantity-button');
    container.appendChild(btn);

    btn.addEventListener('click', async () => {
        btn.classList.add('amazon-update-quantity-button-working');
        const itemNumber = document
            .querySelector('.lineItemCardInfo__itemId.spaceTop')
            .querySelectorAll('.sh-secondary')[1].innerText;
        btn.innerText = 'In Progress';

        try {
            const response = await new Promise(resolve => {
                chrome.runtime.sendMessage(
                    { type: 'increaseQuantityOfItem', itemNumber },
                    reply => { console.log(reply); resolve(reply); }
                );
            });

            btn.classList.remove('amazon-update-quantity-button-working');
            if (!response || !response.response) throw new Error('Response Error');

            btn.classList.add('amazon-update-quantity-button-success');
            btn.innerText = 'Updated';
            btn.dispatchEvent(new CustomEvent('increase:done', { detail: { ok: true, response } }));
        } catch (err) {
            btn.classList.remove('amazon-update-quantity-button-working');
            btn.classList.add('amazon-update-quantity-button-error');
            btn.innerText = 'Error';
            console.log('error', err);
            btn.dispatchEvent(new CustomEvent('increase:done', { detail: { ok: false, error: err } }));
        }
    });

    // Quantity select dropdown (0–10)
    const select = document.createElement('select');
    select.classList.add('amazon-quantity-select');
    for (let i = 0; i <= 10; i++) {
        const opt = document.createElement('option');
        opt.value = i;
        opt.text = i;
        select.appendChild(opt);
    }

    chrome.storage.local.get('quantityToUpdate', result => {
        if (result.quantityToUpdate) select.value = result.quantityToUpdate;
        else { select.value = 1; chrome.storage.local.set({ quantityToUpdate: 1 }); }
    });

    select.addEventListener('change', () => {
        chrome.storage.local.set({ quantityToUpdate: select.value });
    });

    container.appendChild(select);
    return container;
}

// "Copy ETA Message" button
function createCopyEtaButton() {
    const btn = document.createElement('button');
    btn.innerText = 'Copy ETA Message';
    btn.classList.add('amazon-copy-eta-button');

    btn.addEventListener('click', async () => {
        const etaDate = getEtaDateString();
        let { etaMessage } = await chrome.storage.local.get('etaMessage');

        if (!etaMessage) {
            const res = await fetch(chrome.runtime.getURL(
                'content/ebay/mesh_order_details/eta_options/eta_message_default.txt'
            ));
            etaMessage = await res.text();
        }

        let customerName = document.querySelectorAll('.shipping-address .tooltip')[1].innerText;
        customerName = customerName.split(' ')[0];
        customerName = customerName.charAt(0).toUpperCase() + customerName.slice(1).toLowerCase();

        etaMessage = etaMessage.replace('{{Customer_Name}}', customerName).replace('{{Delivery_Date}}', etaDate);
        navigator.clipboard.writeText(etaMessage);
        btn.classList.add('amazon-copy-eta-button-clicked');
        btn.innerText = 'Copied';
    });

    return btn;
}

// "Copy Feedback Message" button
function createCopyFeedbackButton() {
    const btn = document.createElement('button');
    btn.innerText = 'Copy Feedback Message';
    btn.classList.add('amazon-copy-feedback-button');

    btn.addEventListener('click', async () => {
        let { feedbackMessage } = await chrome.storage.local.get('feedbackMessage');

        if (!feedbackMessage) {
            const res = await fetch(chrome.runtime.getURL(
                'content/ebay/mesh_order_details/feedback_options/feedback_message_default.txt'
            ));
            feedbackMessage = await res.text();
        }

        const customerFirstName = (await getOrderDetails()).customer.name.split(' ')[0];
        const message = feedbackMessage.replace('{{Customer_Name}}', customerFirstName);
        navigator.clipboard.writeText(message);
    });

    return btn;
}

// ETA date-picker + link row
function createEtaPickerRow() {
    const input = document.createElement('input');
    input.setAttribute('type', 'date');
    input.classList.add('amazon-eta-field');

    const label = document.createElement('label');
    label.innerText = 'ETA';
    label.setAttribute('for', 'amazon-eta-field');
    label.classList.add('amazon-eta-label');

    const div = document.createElement('div');
    div.appendChild(label);
    div.appendChild(input);

    const link = document.createElement('a');
    link.innerText = 'Change ETA Message';
    link.classList.add('amazon-eta-link');
    link.setAttribute('target', '_blank');
    link.setAttribute(
        'href',
        chrome.runtime.getURL('content/ebay/mesh_order_details/eta_options/eta_options.html')
    );
    div.appendChild(link);
    return div;
}

// Feedback-message link row
function createFeedbackLinkRow() {
    const div = document.createElement('div');
    const link = document.createElement('a');
    link.innerText = 'Change Feedback Message';
    link.classList.add('amazon-feedback-link');
    link.setAttribute('target', '_blank');
    link.setAttribute(
        'href',
        chrome.runtime.getURL('content/ebay/mesh_order_details/feedback_options/feedback_options.html')
    );
    div.appendChild(link);
    return div;
}

// Read the ETA date-picker value and return a human-readable string
function getEtaDateString() {
    const input = document.querySelector('.amazon-eta-field');
    const date = new Date(input.value);
    date.setDate(date.getDate() + 1);
    return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
}

// Build an Amazon product URL from a base-64-encoded ASIN
async function buildAmazonLink(base64Sku) {
    const { domain } = await chrome.storage.local.get('domain');
    let url = `https://www.amazon.${domain}/dp/${atob(base64Sku)}?th=1&psc=1`;
    const { applyAmazonAffiliateTag } = await chrome.storage.local.get('applyAmazonAffiliateTag');
    if (applyAmazonAffiliateTag) url += '&tag=bestbatteri00-20';
    return url;
}

// Submit an eBay order to the SilentSniper API
async function submitEbayOrder(orderData) {
    const { email } = await chrome.storage.local.get('email');
    orderData.email = email;
    try {
        const res = await fetch(
            'https://us-central1-silentsnipe-ce4ca.cloudfunctions.net/app/api/v1/ebayOrders',
            { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(orderData) }
        );
        if (!res.ok) throw new Error('Failed to submit eBay order');
        const result = await res.json();
        console.log('eBay order submitted successfully:', result);
        return result;
    } catch (err) {
        console.error('Error submitting eBay order:', err);
        throw err;
    }
}

// Parse the shipping address from the page DOM
function parseShippingAddress() {
    const addressEl = document.querySelector('.shipping-address .address');
    console.log('shippingAddressElement:', addressEl);
    const children = addressEl.childNodes;
    console.log('children:', children);

    const name = children[0].innerText;
    console.log('name:', name);

    let line1, line2, city, state, zip, country;

    if (children.length === 5) {
        line1 = children[1].innerText;
        console.log('line_1:', line1);
        line2 = children[2].innerText;
        console.log('line_2:', line2);
        const copyNodes = children[3].querySelectorAll('.copy-to-clipboard');
        city = copyNodes[0].innerText;
        state = copyNodes[1].innerText;
        zip = copyNodes[2].innerText;
        country = children[4].innerText;
    } else if (children.length === 4) {
        line1 = children[1].innerText;
        const copyNodes = children[2].querySelectorAll('.copy-to-clipboard');
        city = copyNodes[0].innerText;
        state = copyNodes[1].innerText;
        zip = copyNodes[2].innerText;
        country = children[3].innerText;
    }

    const phoneEl = document.querySelector('.phone.ship-itm');
    console.log('phoneNumberElement:', phoneEl);
    let phone = '';
    if (phoneEl) {
        phone = phoneEl.querySelector('.info-value').innerText;
        console.log('phone:', phone);
    }

    const customer = {
        name,
        phone,
        address: { line_1: line1, line_2: line2, city, state, zip, country },
    };
    console.log('customer:', customer);
    return customer;
}

// Get the buyer's note from the page
function getBuyerNote() {
    const el = document.querySelector('.note-content');
    return el ? el.innerText : null;
}

// Read fulfillment details injected into the page by importFulfillmentDetails()
function readFulfillmentDetails() {
    const amazonOrderNumber = document.querySelector('#amazonOrderNumber')?.innerText;
    const totalBeforeTax = document.querySelector('#totalBeforeTax')?.innerText;
    const totalTax = document.querySelector('#totalTax')?.innerText;
    const estimatedDeliveryDate = document.querySelector('#estimatedDeliveryDate')?.innerText;
    const amazonEmail = document.querySelector('#amazonEmail')?.innerText;
    const fulfillmentDate = document.querySelector('#fulfillmentDate')?.innerText;
    const amazonSku = document.querySelector('#amazonSku')?.innerText;
    const amazonDomain = document.querySelector('#amazonDomain')?.innerText;

    if (amazonOrderNumber && totalBeforeTax && totalTax && estimatedDeliveryDate && amazonEmail && fulfillmentDate) {
        return { amazonOrderNumber, totalBeforeTax, totalTax, estimatedDeliveryDate, amazonEmail, fulfillmentDate, amazonSku, amazonDomain };
    }
}

// "Export to SilentSniper" button
function createExportButton() {
    const btn = document.createElement('button');
    btn.innerText = 'Export to SilentSniper';
    btn.classList.add('ebay-submit-order-details-button');

    btn.addEventListener('click', async () => {
        btn.classList.add('ebay-submit-order-details-button-working');
        const orderDetails = await getOrderDetails();
        console.log('orderDetails', orderDetails);
        try {
            const response = await submitEbayOrder(orderDetails);
            console.log('response', response);
            btn.classList.remove('ebay-submit-order-details-button-working');
            btn.classList.add('ebay-submit-order-details-button-success');
            btn.innerText = 'Exported!';
        } catch (err) {
            btn.classList.remove('ebay-submit-order-details-button-working');
            btn.classList.add('ebay-submit-order-details-button-error');
            btn.innerText = 'Error Exporting Data';
            console.log('error', err);
        }
    });

    return btn;
}

// Submit the current order silently
async function submitCurrentOrder() {
    const orderDetails = await getOrderDetails();
    console.log('orderDetails', orderDetails);
    try {
        const response = await submitEbayOrder(orderDetails);
        console.log('response', response);
    } catch (err) {
        console.log('error', err);
    }
}

// Build and send a sheet row via Google Apps Script
async function sendToSpreadsheetRow() {
    const orderDetails = await getOrderDetails();
    console.log('orderDetails', orderDetails);

    const rowValues = [
        null,
        null,
        '7999',
        orderDetails.ebayOrderNumber,
        orderDetails.ebaySku,
        orderDetails.customer.name,
        orderDetails.customer.address.line_1,
        orderDetails.customer.address.line_2,
        orderDetails.customer.address.city,
        orderDetails.customer.address.state,
        orderDetails.customer.address.zip,
        orderDetails.customer.phone,
        orderDetails.quantitySold,
        orderDetails.dateOfSale,
        orderDetails.itemName,
        orderDetails.soldPrice,
        orderDetails.ebayFees,
        orderDetails.addFee,
    ];
    console.log('rowValues', rowValues);

    const response = await new Promise(resolve => {
        chrome.runtime.sendMessage({ type: 'createSheetRow', rowValues }, reply => {
            console.log(reply);
            resolve(reply);
        });
    });

    console.log('response', response);

    if (response && response.result === 'success') {
        const rowNumber = response.rowNumber;
        const indicator = document.createElement('div');
        indicator.id = 'googleSheetRowNumber';
        indicator.value = rowNumber;
        indicator.innerHTML = `Row Number: ${rowNumber}`;
        indicator.style.color = 'green';
        indicator.style.fontWeight = 'bold';
        getItemIdElement().appendChild(indicator);
    }
    return response;
}

// Open all action links in new tabs
async function openActionsInNewTabs() {
    document.querySelectorAll('.actions a').forEach(a => a.setAttribute('target', '_blank'));
}

// "Import Fulfillment Details" button
function createImportButton() {
    const btn = document.createElement('button');
    btn.innerHTML = 'Import Fulfillment Details';
    btn.className = 'importButton';
    btn.onclick = async () => importFulfillmentDetails();
    return btn;
}

// Import fulfillment details from virtual clipboard or local clipboard
async function importFulfillmentDetails(fromVirtualClipboard = false, clipKey) {
    try {
        let fulfillmentData;
        if (fromVirtualClipboard) {
            let res = await fetch(
                `'https://us-central1-silentsnipe-ce4ca.cloudfunctions.net/app/api/v1/virtualClipboard/${clipKey}`
            );
            res = await res.json();
            console.log('response', res);
            console.log(res.data.content);
            fulfillmentData = JSON.parse(res.data.content);
            console.log('fullfilmentData', fulfillmentData);
        } else {
            const text = await navigator.clipboard.readText();
            fulfillmentData = JSON.parse(text);
        }
        await renderFulfillmentDetails(fulfillmentData);
    } catch (err) {
        console.error('Error importing fulfillment details:', err);
        throw err;
    }
}

// Inject fulfillment details HTML into the page
async function renderFulfillmentDetails(data) {
    const html = `
    <div class="fulfillmentDetails">
      <h2>Fulfillment Details</h2>
      <p><strong>Customer Address:</strong> <span id="customerAddress">${data.customerAddress.replace(/\n/g, '<br>')}</span></p>
      <p><strong>Order Number:</strong> <span id="amazonOrderNumber">${data.amazonOrderNumber}</span></p>
      <p><strong>Total Before Tax:</strong> <span id="totalBeforeTax">${data.totalBeforeTax}</span></p>
      <p><strong>Total Tax:</strong> <span id="totalTax">${data.totalTax}</span></p>
      <p><strong>Estimated Delivery Date:</strong> <span id="estimatedDeliveryDate">${data.estimatedDeliveryDate}</span></p>
      <p><strong>Amazon Email:</strong> <span id="amazonEmail">${data.amazonEmail}</span></p>
      <p><strong>Fulfillment Date:</strong> <span id="fulfillmentDate">${data.fulfillmentDate}</span></p>
      <p><strong>Amazon SKU:</strong> <span id="amazonSku">${data.amazonSku}</span></p>
      <p><strong>Amazon Domain:</strong> <span id="amazonDomain">${data.amazonDomain}</span></p>
    </div>
  `;

    const wrapper = document.createElement('div');
    wrapper.innerHTML = html;
    document.querySelector('.soldPrice').parentElement.parentElement.appendChild(wrapper);

    const orderDetails = await getOrderDetails();
    warnIfMismatch(
        orderDetails.customer.name,
        data.customerAddress.split('\n')[0],
        atob(orderDetails.ebaySku),
        data.amazonSku
    );

    const etaDate = parseDateFromText(document.querySelector('#estimatedDeliveryDate').innerText);
    if (etaDate) { console.log('Date found:', etaDate); updateEtaField(etaDate); }
    else console.log('No date found');

    const profit = await calculateProfit(orderDetails.orderEarnings, data.totalBeforeTax, data.totalTax, data.amazonDomain);
    console.log('Profit:', profit);
}

// Warn if customer name or SKU doesn't match
function warnIfMismatch(ebayName, amazonName, ebaySku, amazonSku) {
    if (amazonName !== ebayName)
        alert('WARNING: Customer name does not match! Please verify the fulfillment details.');
    if (ebaySku !== amazonSku)
        alert('WARNING: SKU does not match! Please verify the fulfillment details.');
}


// ─────────────────────────────────────────────
// Date parsing utilities
// ─────────────────────────────────────────────

function parseDateFromText(text) {
    const now = new Date();
    const DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

    function addDays(date, n) {
        const d = new Date(date);
        d.setDate(d.getDate() + n);
        return d;
    }

    function setTime(date, timeStr) {
        let [timePart, ampm] = timeStr.split(' ');
        let [hours, minutes] = timePart.split(':');
        if (ampm === 'PM' && hours < 12) hours = parseInt(hours) + 12;
        date.setHours(hours, minutes || 0, 0, 0);
        return date;
    }

    function parseDayMonthDate(str) {
        const [dayName, rest] = str.split(', ');
        const [day, shortMonth] = rest.split(' ');
        const monthIndex = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'].indexOf(shortMonth);
        if (monthIndex === -1) return null;
        const year = now.getMonth() > monthIndex ? now.getFullYear() + 1 : now.getFullYear();
        return new Date(year, monthIndex, day);
    }

    const patterns = [
        { regex: /Delivered today/, date: new Date(now) },
        { regex: /Arriving today/, date: new Date(now) },
        { regex: /Arriving tomorrow/, date: addDays(new Date(now), 1) },
        { regex: /Delivered (\w+ \d+)/, dateFunction: m => new Date(m[1] + ', ' + now.getFullYear()) },
        {
            regex: /Arriving (\w+)$/,
            dateFunction: m => {
                const dayIndex = DAYS.indexOf(m[1]);
                const d = new Date(now);
                d.setDate(d.getDate() + (7 + dayIndex - now.getDay()) % 7);
                return d;
            },
        },
        { regex: /Now expected by (\w+ \d+)/, dateFunction: m => new Date(m[1] + ', ' + now.getFullYear()) },
        { regex: /Arriving tomorrow by (\d+ [AP]M)/, dateFunction: m => setTime(addDays(new Date(now), 1), m[1]) },
        { regex: /Arriving today by (\d+ [AP]M)/, dateFunction: m => setTime(new Date(now), m[1]) },
        { regex: /Arriving by (\w+ \d+)/, dateFunction: m => new Date(m[1] + ', ' + now.getFullYear()) },
        { regex: /Delivered (\w+ \d+),/, dateFunction: m => new Date(m[1] + ', ' + now.getFullYear()) },
        { regex: /Expected by (\w+ \d+)/, dateFunction: m => new Date(m[1] + ', ' + now.getFullYear()) },
        { regex: /Arriving (\w+, \d+ \w+)/, dateFunction: m => parseDayMonthDate(m[1]) },
    ];

    for (const { regex, date, dateFunction } of patterns) {
        const match = text.match(regex);
        if (match) return dateFunction ? dateFunction(match) : date;
    }
    return null;
}

// Set the ETA date-picker to a given Date
function updateEtaField(date) {
    if (!(date instanceof Date) || isNaN(date)) {
        console.error('updateEta: Invalid date provided', date);
        return;
    }
    const formatted = [
        date.getFullYear(),
        ('0' + (date.getMonth() + 1)).slice(-2),
        ('0' + date.getDate()).slice(-2),
    ].join('-');
    console.log('updateEta: Formatted date', formatted);

    const input = document.querySelector('.amazon-eta-field');
    if (!input) { console.error('updateEta: .amazon-eta-field element not found'); return; }
    if (input.disabled || input.readOnly) { console.error('updateEta: .amazon-eta-field is disabled or read-only'); return; }
    input.value = formatted;
    console.log('updateEta: ETA updated successfully');
}


// ─────────────────────────────────────────────
// Currency conversion (static rates, for reference only)
// ─────────────────────────────────────────────

const CURRENCY_RATES = {
    CAD_TO_USD: 1 / 1.3507,
    USD_TO_CAD: 1.3507,
    GBP_TO_USD: 1 / 0.789173,
    USD_TO_GBP: 0.789173,
    AUD_TO_USD: 1 / 1.64866,
    USD_TO_AUD: 1.64866,
    EUR_TO_USD: 1 / 1.0821,
    USD_TO_EUR: 1.0821,
};

function parseCurrencyString(str) {
    const symbolMatch = str.match(/(C \$|\$|£|A \$|€)/);
    return {
        currencySymbol: symbolMatch ? symbolMatch[0] : null,
        amount: parseFloat(str.replace(/[^\d.-]/g, '')),
    };
}

function domainToCurrency(domain) {
    switch (domain) {
        case 'www.amazon.ca': return 'CAD';
        case 'www.amazon.com': return 'USD';
        case 'www.amazon.co.uk': return 'GBP';
        case 'www.amazon.com.au': return 'AUD';
        case 'www.amazon.de': return 'EUR';
        default: return 'UNKNOWN';
    }
}

function stripCurrencySymbol(str) {
    return parseFloat(str.replace(/[^\d.-]/g, ''));
}

// Close any open VOD (lightbox) dialog
async function closeVodDialog() {
    const btn = document.querySelector('.vod-dialog button.lightbox-dialog__close');
    if (btn) btn.click();
}

// Write a note on the eBay order
async function writeNote(noteText) {
    const editBtn = document.querySelector('.edit-note');
    console.log('editNoteElement', editBtn);

    if (editBtn) editBtn.click();
    else {
        const addBtn = document.querySelector('[data-action-id="ADD_NOTE"]');
        console.log('addNote', addBtn);
        addBtn.click();
    }

    const textarea = await waitForElementVanillaJs('textarea[name="note-content"]');
    textarea.value = noteText;
    textarea.dispatchEvent(new Event('input', { bubbles: true }));
    textarea.dispatchEvent(new Event('change', { bubbles: true }));

    await new Promise(r => setTimeout(r, 1000));

    const saveBtn = textarea.closest('.lightbox-dialog__window').querySelector('.btn.btn--primary');
    console.log('saveButton', saveBtn);
    saveBtn.dispatchEvent(new MouseEvent('click', { bubbles: true }));

    await new Promise(r => setTimeout(r, 1000));
    await closeVodDialog();
}

// "Add Note" button
function createAddNoteButton() {
    const btn = document.createElement('button');
    btn.innerText = 'Add Note';
    btn.classList.add('add-note-button');
    btn.addEventListener('click', async () => await createAndAppendNote());
    return btn;
}

// Build and write the auto-note
async function writeAutoNote() {
    const existingNote = getExistingNote();
    const newPart = await buildNoteText();
    const combined = existingNote ? existingNote + '\n' + newPart : newPart;
    await writeNote(combined);
}

// Return existing note text, if any
function getExistingNote() {
    const el = document.querySelector('.note-content');
    return el ? el.innerText : null;
}

// Build the note text from fulfillment data
async function buildNoteText() {
    const amazonEmail = document.querySelector('#amazonEmail').innerText;
    const estimatedDeliveryDate = document.querySelector('#estimatedDeliveryDate').innerText;
    const fulfillmentDate = document.querySelector('#fulfillmentDate').innerText;
    const { agentName } = await chrome.storage.local.get('agentName');
    return `SS/${amazonEmail}/${estimatedDeliveryDate}/${fulfillmentDate}-${agentName}`;
}


// ─────────────────────────────────────────────
// Help-popover (tooltip) component
// ─────────────────────────────────────────────

const helpPopover = (() => {
    let popoverEl = null;
    let openForEl = null;
    let hideTimer = null;

    function getOrCreatePopover() {
        if (!popoverEl) {
            popoverEl = document.createElement('div');
            popoverEl.className = 'sniper-help-popover-portal';
            popoverEl.setAttribute('hidden', '');
            document.body.appendChild(popoverEl);

            popoverEl.addEventListener('mouseenter', () => {
                if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }
            });
            popoverEl.addEventListener('mouseleave', () => scheduleHide(120));
            popoverEl.addEventListener('click', e => e.stopPropagation());
        }
        return popoverEl;
    }

    function positionPopover(anchorEl) {
        const el = getOrCreatePopover();
        const rect = anchorEl.getBoundingClientRect();
        const vw = window.innerWidth;
        const vh = window.innerHeight;

        el.style.visibility = 'hidden';
        el.removeAttribute('hidden');

        const elW = el.offsetWidth;
        const elH = el.offsetHeight;

        let left = Math.min(Math.max(rect.left, 8), vw - elW - 8);
        let top = rect.bottom + 8;

        if (top + elH > vh - 8) top = Math.max(rect.top - 8 - elH, 8);
        if (left + elW > vw - 8) left = vw - elW - 8;
        if (left < 8) left = 8;

        el.style.left = left + 'px';
        el.style.top = top + 'px';
        el.style.visibility = 'visible';
    }

    function openPopover(anchor, content) {
        if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }
        getOrCreatePopover().textContent = String(content || '');
        openForEl = anchor;
        positionPopover(anchor);
        document.addEventListener('click', onDocClick, true);
        document.addEventListener('keydown', onKeydown, true);
        window.addEventListener('scroll', onScrollOrResize, true);
        window.addEventListener('resize', onScrollOrResize);
    }

    function closePopover() {
        getOrCreatePopover().setAttribute('hidden', '');
        openForEl = null;
        document.removeEventListener('click', onDocClick, true);
        document.removeEventListener('keydown', onKeydown, true);
        window.removeEventListener('scroll', onScrollOrResize, true);
        window.removeEventListener('resize', onScrollOrResize);
    }

    function togglePopover(anchor, content) {
        openForEl === anchor ? closePopover() : openPopover(anchor, content);
    }

    function scheduleHide(ms) {
        hideTimer = setTimeout(closePopover, ms);
    }

    function onDocClick(e) {
        if (!openForEl) return;
        if (!openForEl.contains(e.target) && !getOrCreatePopover().contains(e.target)) closePopover();
    }

    function onKeydown(e) {
        if (e.key === 'Escape') closePopover();
    }

    function onScrollOrResize() {
        if (openForEl) positionPopover(openForEl);
    }

    return {
        makeIcon(helpText) {
            const icon = document.createElement('span');
            icon.className = 'sniper-help-icon';
            icon.tabIndex = 0;
            icon.setAttribute('role', 'button');
            icon.setAttribute('aria-label', 'More info');
            icon.textContent = 'i';

            icon.addEventListener('click', e => { e.stopPropagation(); togglePopover(icon, helpText); });
            icon.addEventListener('mouseenter', () => openPopover(icon, helpText));
            icon.addEventListener('mouseleave', () => scheduleHide(120));
            icon.addEventListener('keydown', e => {
                if (e.key === 'Enter' || e.key === ' ') {
                    e.preventDefault();
                    togglePopover(icon, helpText);
                }
            });
            return icon;
        },
    };
})();


// ─────────────────────────────────────────────
// Auto-Order settings + button
// ─────────────────────────────────────────────

async function createAutoOrderWidget() {
    // Settings definitions
    const SETTINGS = [
        {
            key: 'shouldGetLinkInstead',
            type: 'checkbox',
            label: 'Copy Auto Link instead of Auto Order',
            defaultValue: false,
            includeInOrderDetails: false,
            help: 'Copies a special Amazon checkout link with your order details so you can place the order from another Chrome profile/device. That other browser must have SilentSniper installed. Tip: set the Amazon email in that profile\'s Options → Order Settings so SilentSniper can attribute/track the order to the right Amazon account. No auto order happens in this browser.',
        },
        {
            key: 'shouldUseGiftOption',
            type: 'checkbox',
            label: 'Checkout as Gift',
            defaultValue: false,
            includeInOrderDetails: true,
            help: 'Uses Amazon\'s gift checkout option. This hides the item price and lets you include your own Gift Message.',
        },
        {
            key: 'giftMessage',
            type: 'textarea',
            label: 'Gift Message',
            defaultValue: '-',
            includeInOrderDetails: true,
            help: 'Text shown on Amazon\'s gift slip when you use \'Checkout as Gift\'.',
        },
        {
            key: 'giftMessageSender',
            type: 'text',
            label: 'Gift Message Sender',
            defaultValue: '-',
            includeInOrderDetails: true,
            help: 'Name shown as the sender on the gift slip.',
        },
        {
            key: 'autoConfirmPurchase',
            type: 'checkbox',
            label: 'Place order automatically (skip final click)',
            defaultValue: false,
            includeInOrderDetails: true,
            help: 'Automatically places the order on Amazon\'s final review page, skipping the last manual click. Make sure your payment details are correct in Amazon beforehand.',
        },
        {
            key: 'sendToSpreadsheet',
            type: 'checkbox',
            label: 'Auto Bookkeeping (Spreadsheet)',
            defaultValue: false,
            includeInOrderDetails: true,
            help: 'Automatically logs each order into your connected SilentSniper bookkeeping spreadsheet. Saves all sales, fees, costs, and profit data — no more manual entry.',
        },
        {
            key: 'shouldSendETAWithAI',
            type: 'checkbox',
            label: 'Send ETA message (AI)',
            defaultValue: false,
            includeInOrderDetails: false,
            help: 'Generates a short, friendly delivery update and sends it to the buyer on eBay. Language is inferred from the site/country. You can customize the tone/prompt in Options → Order Settings.',
        },
        {
            key: 'shouldMarkAsShipped',
            type: 'checkbox',
            label: 'Mark eBay order as shipped automatically',
            defaultValue: false,
            includeInOrderDetails: false,
        },
        {
            key: 'shouldIncreaseQuantity',
            type: 'checkbox',
            label: 'Increase quantity of eBay order automatically',
            defaultValue: false,
            includeInOrderDetails: false,
        },
        {
            key: 'shouldWriteNote',
            type: 'checkbox',
            label: 'Write note on eBay order',
            defaultValue: false,
            includeInOrderDetails: false,
            help: 'Adds a note to the eBay order (e.g., Amazon email, ETA, date, your agent name). You can switch to a custom template in Options → Custom Note Template; tokens like {{amazonEmail}}, {{eta}}, {{date}}, {{agent}} are supported.',
        },
        {
            key: 'autoCloseAmazonTabAfterOrder',
            type: 'checkbox',
            label: 'Auto-close Amazon tab after order',
            defaultValue: false,
            includeInOrderDetails: true,
            help: 'Closes the Amazon tab a few seconds after the order completes—useful for multi-tasking or if you place many orders at once.',
        },
        {
            key: 'agentName',
            type: 'text',
            label: 'Your Name (for notes)',
            defaultValue: 'Agent',
            includeInOrderDetails: false,
            help: 'Used in eBay order notes (e.g., "– Aug 29, 2025 Sammy"). Useful if you have VAs or team members placing orders.',
        },
    ];

    // Build defaults object
    function buildDefaults() {
        const defaults = {};
        for (const setting of SETTINGS) defaults[setting.key] = setting.defaultValue;
        return defaults;
    }

    // In-memory settings store with Chrome storage sync
    const store = {
        currentValues: buildDefaults(),

        async loadFromChromeStorage() {
            const defaults = buildDefaults();
            const stored = await chrome.storage.local.get(defaults);
            for (const key in defaults) {
                if (Object.prototype.hasOwnProperty.call(stored, key)) this.currentValues[key] = stored[key];
            }
            notifyListeners();
            return this.getSnapshot();
        },

        async saveSingleSettingToChromeStorage(key, value) {
            this.currentValues[key] = value;
            await chrome.storage.local.set({ [key]: value });
            notifyListeners();
        },

        getSnapshot() {
            return JSON.parse(JSON.stringify(this.currentValues));
        },
    };

    const listeners = [];

    function notifyListeners() {
        const snapshot = store.getSnapshot();
        for (const fn of listeners) {
            try { fn(snapshot); } catch { }
        }
    }

    // Copy settings-flagged keys into the order-details object
    function applySettingsToOrderDetails(orderDetails, snapshot) {
        for (const setting of SETTINGS) {
            if (setting.includeInOrderDetails) {
                orderDetails[setting.orderDetailsKey || setting.key] = snapshot[setting.key];
            }
        }
        return orderDetails;
    }

    await store.loadFromChromeStorage();

    // ── Root container ──
    const container = document.createElement('div');
    container.className = 'auto-order-container';

    // ── Auto-Order / Copy Auto Link button ──
    const actionBtn = (() => {
        const btn = document.createElement('button');
        btn.className = 'auto-order-button';
        btn.id = 'autoOrderButton';
        btn.textContent = store.currentValues.shouldGetLinkInstead ? 'Copy Auto Link' : 'Auto Order';

        listeners.push(snapshot => {
            btn.textContent = snapshot.shouldGetLinkInstead ? 'Copy Auto Link' : 'Auto Order';
        });

        btn.addEventListener('click', async e => {
            e.preventDefault();
            btn.disabled = true;
            try {
                const snapshot = store.getSnapshot();

                if (snapshot.shouldGetLinkInstead) {
                    const sku = await getSku();
                    if (!sku) { alert('SKU not found. Please enter the SKU manually.'); return; }
                    let link = await buildAmazonLink(sku);
                    const orderDetails = await getOrderDetails();
                    applySettingsToOrderDetails(orderDetails, snapshot);
                    link = `${link}?autoOrder=true&orderDetails=${encodeURIComponent(JSON.stringify(orderDetails))}`;
                    await navigator.clipboard.writeText(link);
                    btn.classList.add('amazon-copy-link-button-clicked');
                    setTimeout(() => btn.classList.remove('amazon-copy-link-button-clicked'), 1200);
                    if (snapshot.sendToSpreadsheet) await runFullOrderFlow(orderDetails);
                    btn.dispatchEvent(new CustomEvent('order:done', {
                        detail: { ok: true, profit: document.querySelector('.profit-final .value-visible')?.innerText },
                    }));
                } else {
                    const orderDetails = await getOrderDetails();
                    applySettingsToOrderDetails(orderDetails, snapshot);
                    await sendOrderSameBrowser(orderDetails);
                    if (snapshot.sendToSpreadsheet) await runFullOrderFlow(orderDetails);
                    btn.dispatchEvent(new CustomEvent('order:done', {
                        detail: { ok: true, profit: document.querySelector('.profit-final .value-visible')?.innerText },
                    }));
                }
            } catch (err) {
                console.error('Auto order action error:', err);
                btn.dispatchEvent(new CustomEvent('order:done', { detail: { ok: false } }));
            } finally {
                btn.disabled = false;
            }
        });

        return btn;
    })();

    // ── Settings modal ──
    const modal = (() => {
        const overlay = document.createElement('div');
        overlay.className = 'settings-modal';
        overlay.setAttribute('hidden', '');

        const content = document.createElement('div');
        content.className = 'settings-modal-content';

        const closeBtn = document.createElement('span');
        closeBtn.className = 'settings-modal-close';
        closeBtn.textContent = '×';
        closeBtn.addEventListener('click', () => overlay.setAttribute('hidden', ''));

        content.appendChild(closeBtn);
        overlay.appendChild(content);
        overlay.addEventListener('click', e => { if (e.target === overlay) overlay.setAttribute('hidden', ''); });

        return { overlay, content };
    })();

    // ── Settings form ──
    const form = (() => {
        const grid = document.createElement('div');
        grid.style.display = 'grid';
        grid.style.gridTemplateColumns = '1fr';
        grid.style.rowGap = '10px';

        for (const setting of SETTINGS) {
            if (setting.type === 'checkbox') {
                const row = document.createElement('div');
                row.style.cssText = 'display:flex;align-items:center;gap:8px';

                const lbl = document.createElement('label');
                lbl.style.cssText = 'display:inline-flex;align-items:center;gap:8px;flex:1 1 auto';

                const chk = document.createElement('input');
                chk.type = 'checkbox';
                chk.checked = !!store.currentValues[setting.key];

                chk.addEventListener('change', async () => {
                    await store.saveSingleSettingToChromeStorage(setting.key, chk.checked);
                });

                const span = document.createElement('span');
                span.textContent = setting.label;
                lbl.appendChild(chk);
                lbl.appendChild(span);

                if (setting.help) {
                    const icon = helpPopover.makeIcon(setting.help);
                    icon.style.marginLeft = '2px';
                    row.appendChild(lbl);
                    row.appendChild(icon);
                } else {
                    row.appendChild(lbl);
                }
                grid.appendChild(row);

            } else if (setting.type === 'text' || setting.type === 'textarea') {
                const wrapper = document.createElement('div');
                wrapper.style.cssText = 'display:grid;row-gap:6px';

                const header = document.createElement('div');
                header.style.cssText = 'display:flex;align-items:center;gap:6px';

                const title = document.createElement('div');
                title.textContent = setting.label;
                title.style.fontWeight = '600';
                header.appendChild(title);

                if (setting.help) header.appendChild(helpPopover.makeIcon(setting.help));

                let input;
                if (setting.type === 'textarea') {
                    input = document.createElement('textarea');
                    input.rows = setting.rows || 3;
                } else {
                    input = document.createElement('input');
                    input.type = 'text';
                }
                input.value = store.currentValues[setting.key] || '';
                if (setting.placeholder) input.placeholder = setting.placeholder;
                input.style.width = '100%';

                input.addEventListener('input', async () => {
                    await store.saveSingleSettingToChromeStorage(setting.key, input.value);
                });

                wrapper.appendChild(header);
                wrapper.appendChild(input);
                grid.appendChild(wrapper);
            }
        }
        return grid;
    })();

    modal.content.appendChild(form);
    container.appendChild(actionBtn);

    // Gear (settings) icon
    const gearIcon = document.createElement('span');
    gearIcon.className = 'settings-icon';
    gearIcon.textContent = '⚙';
    gearIcon.title = 'Auto Order Settings';
    gearIcon.addEventListener('click', e => {
        e.preventDefault();
        modal.overlay.removeAttribute('hidden');
    });

    container.appendChild(gearIcon);
    container.appendChild(modal.overlay);
    return container;
}


// ─────────────────────────────────────────────
// Order dispatchers
// ─────────────────────────────────────────────

// Tell the background script to order via the same browser
async function sendOrderSameBrowser(orderDetails) {
    chrome.runtime.sendMessage({ type: 'order_item_same_browser', orderDetails });
}

// Get the raw item number from the page
function getItemNumber() {
    return document
        .querySelector('.lineItemCardInfo__itemId')
        .querySelectorAll('.sh-secondary')[1].innerText;
}

// Resolve a SKU from the item description via the background script
async function getSkuFromDescription(itemNumber) {
    const response = await new Promise(resolve => {
        chrome.runtime.sendMessage(
            { type: 'get_sku_from_description', itemNumber },
            reply => resolve(reply)
        );
    });
    return response?.sku;
}

// Update a Google Sheet via Apps Script
async function updateGoogleSheet(spreadsheetId, range, values) {
    const payload = { spreadsheetId, range, values };
    try {
        const res = await fetch(
            'https://script.google.com/macros/s/AKfycbwTOktup4oRvsKGNPtMSwUTNX41Z93HZt_PqFDzzgu0jPysi9Tmmrcs-LPf86z2nc8tnQ/exec',
            { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }
        );
        const result = await res.json();
        console.log('Update result:', result);
    } catch (err) {
        console.error('Error updating sheet:', err);
    }
}

// Build the utility-buttons row
async function createUtilityButtons() {
    const row = document.createElement('div');
    row.classList.add('utility-buttons');

    const copyBtn = await createCopyLinkButton();
    const autoOrder = await createAutoOrderWidget();

    if (copyBtn) {
        const automationDiv = document.createElement('div');
        automationDiv.classList.add('automation-div');
        automationDiv.appendChild(copyBtn);
        automationDiv.appendChild(autoOrder);
        row.appendChild(automationDiv);
    }

    row.appendChild(createQuantityWidget());
    return row;
}


// ─────────────────────────────────────────────
// Full order flow (post-order automation)
// ─────────────────────────────────────────────

async function runFullOrderFlow(orderDetails) {
    console.log('order details', orderDetails);

    autoOrderButtonQueue = createButtonTextQueue('autoOrderBtn', 1200, {
        titlePrefix: 'Auto Order',
        mount: 'body',
        autoShowHud: true,
        fadeOutMs: 1500,
    });

    document.title = 'Auto Order Begins';
    document.getElementById('autoOrderButton').disabled = true;
    autoOrderButtonQueue.update('Ordering...');

    let attempts = 0;
    let submitted = false;
    let submissionResponse;

    while (!submitted && attempts < 3) {
        attempts++;
        if (attempts > 1) {
            autoOrderButtonQueue.update(`Failed to submit order details, retrying (${attempts}/3)...`);
            const currency = tldToCurrency(orderDetails.domain);
            orderDetails.orderEarningsCurrency = currency;
            orderDetails.grandTotalCurrency = currency;
            await new Promise(r => setTimeout(r, 5000));
        }
        try {
            submissionResponse = await submitEbayOrder(orderDetails);
            console.log('submissionResponse', submissionResponse);
            submitted = true;
        } catch (err) {
            console.error('Error submitting eBay order:', err);
        }
    }

    console.log('orderDetails:', orderDetails);
    console.log('Submission response:', submissionResponse);

    const validCodes = ['success', 'duplicate', 'updated', 'created'];
    if (!validCodes.includes(submissionResponse?.code)) {
        console.error('Failed to submit eBay order:', submissionResponse?.error);
        autoOrderButtonQueue.error('Failed to submit order details');
        return;
    }

    // Poll for the Amazon order number
    let fullOrderDetails;
    try {
        autoOrderButtonQueue.update('Waiting For Amazon Order To Be Placed...');
        fullOrderDetails = await pollForAmazonOrder(orderDetails.ebayOrderNumber);
        autoOrderButtonQueue.update('Order Placed!');
    } catch (err) {
        console.error('Error looking up Amazon order:', err);
        autoOrderButtonQueue.error(err);
        return;
    }

    if (fullOrderDetails?.error) {
        autoOrderButtonQueue.error(fullOrderDetails.error);
        console.error('Error looking up Amazon order:', fullOrderDetails.error);
        let noteText = String(fullOrderDetails.error);
        const existing = getExistingNote();
        if (existing) noteText = existing + ' - ' + noteText;
        await writeNote(noteText);
        return;
    }

    console.log('fullOrderDetails:', fullOrderDetails);

    // Write eBay order note
    const { shouldWriteNote } = await chrome.storage.local.get({ shouldWriteNote: false });
    if (shouldWriteNote) {
        autoOrderButtonQueue.update('Creating note...');
        const { useCustomNoteTemplate } = await chrome.storage.local.get({ useCustomNoteTemplate: false });
        const noteText = useCustomNoteTemplate
            ? await buildCustomNoteFromTemplate(fullOrderDetails)
            : await buildDefaultNote(fullOrderDetails);
        await writeNote(noteText);
    }

    // Send ETA message with AI
    const { shouldSendETAWithAI } = await chrome.storage.local.get({ shouldSendETAWithAI: false });
    if (shouldSendETAWithAI) {
        const etaDate = parseDateFromText(fullOrderDetails.estimatedDeliveryDate);
        if (etaDate) {
            autoOrderButtonQueue.update(`ETA: ${etaDate}`);
            console.log('Date found:', etaDate);
            updateEtaField(etaDate);
            autoOrderButtonQueue.update('ETA Updated');
        } else {
            console.log('No date found');
        }

        autoOrderButtonQueue.update('Building ETA message...');
        const { useCustomEtaMessagePrompt } = await chrome.storage.local.get({ useCustomEtaMessagePrompt: false });
        const etaResult = useCustomEtaMessagePrompt
            ? await buildCustomEtaMessage(fullOrderDetails)
            : await buildDefaultEtaMessage(fullOrderDetails);

        if (!etaResult.ok) { console.error('ETA error:', etaResult.error); return; }

        console.log('ETA message:', etaResult.data);
        autoOrderButtonQueue.update('Sending ETA to eBay buyer...');
        await sendEtaToEbayBuyer(etaResult.data);
        autoOrderButtonQueue.update('ETA Sent');
    }

    // Mark as shipped
    const { shouldMarkAsShipped } = await chrome.storage.local.get({ shouldMarkAsShipped: false });
    if (shouldMarkAsShipped) {
        autoOrderButtonQueue.update('Marking item as shipped...');
        if (await markOrderAsShipped()) {
            autoOrderButtonQueue.update('Item marked as shipped');
            await showShippedBanner();
        } else {
            autoOrderButtonQueue.update('Failed to mark item as shipped');
            await new Promise(r => setTimeout(r, 3000));
        }
    }

    // Calculate and animate profit
    let orderEarnings = parseFloat(fullOrderDetails.orderEarnings) || 0;
    let grandTotal = parseFloat(fullOrderDetails.grandTotal) || 0;

    if (needsCurrencyConversion(fullOrderDetails.orderEarningsCurrency, fullOrderDetails.grandTotalCurrency)) {
        autoOrderButtonQueue.update(`Converting ${fullOrderDetails.grandTotalCurrency} to ${fullOrderDetails.orderEarningsCurrency}...`);
        grandTotal = await convertCurrency(fullOrderDetails.grandTotal, fullOrderDetails.grandTotalCurrency, fullOrderDetails.orderEarningsCurrency);
    }

    autoOrderButtonQueue.update('Calculating profit...');
    const profit = calculateProfit(orderEarnings, grandTotal);
    await animateProfitDisplay(profit);

    // Increase quantity if flagged
    const { shouldIncreaseQuantity } = await chrome.storage.local.get({ shouldIncreaseQuantity: false });
    if (shouldIncreaseQuantity) {
        autoOrderButtonQueue.update('Updating quantity...');
        const quantityBtn = document.querySelector('.amazon-update-quantity-button');
        const result = await waitForButtonEvent(quantityBtn, 'increase');
        if (result.ok) autoOrderButtonQueue.update('Quantity Increased');
    }

    autoOrderButtonQueue.finish('Order placed');
}

// Map Amazon TLD to currency code
function tldToCurrency(tld) {
    switch (tld) {
        case 'com': default: return 'USD';
        case 'co.uk': return 'GBP';
        case 'ca': return 'CAD';
        case 'de': case 'fr': case 'it': case 'es': case 'nl': return 'EUR';
        case 'com.mx': return 'MXN';
        case 'com.au': return 'AUD';
        case 'se': return 'SEK';
        case 'pl': return 'PLN';
        case 'in': return 'INR';
    }
}


// ─────────────────────────────────────────────
// Profit display animation
// ─────────────────────────────────────────────

async function animateProfitDisplay(profitValue, options = {}) {
    try {
        const { durationMs, scrollMs = 300, minMs = 100, maxMs = 800 } = options;

        const earningsRow =
            document.querySelector('.payment-info .earnings .total') ||
            document.querySelector('.payment-info .total') ||
            document.querySelector('.payment-info [class*="total"]');

        if (!earningsRow) { console.warn('Order earnings row not found'); return false; }

        let inlineEl = document.querySelector('.profit-inline');
        if (!inlineEl) {
            inlineEl = document.createElement('div');
            inlineEl.className = 'profit-inline';
            inlineEl.innerHTML = `
        <div class="label">Profit</div>
        <div class="value">
          <span class="value-sign"></span>
          <span class="value-int">0</span><span class="value-sep">.</span><span class="value-dec">00</span>
        </div>
        <div class="profit-inline__halo" aria-hidden="true"></div>
      `;
            earningsRow.parentNode.insertBefore(inlineEl, earningsRow.nextSibling);
        }

        const signEl = inlineEl.querySelector('.value-sign');
        const intEl = inlineEl.querySelector('.value-int');
        const decEl = inlineEl.querySelector('.value-dec');
        const valueEl = inlineEl.querySelector('.value');

        const numericValue = Number(profitValue) || 0;
        const isNegative = numericValue < 0;
        const absValue = Math.abs(numericValue);

        inlineEl.classList.toggle('profit-inline--neg', isNegative);
        signEl.textContent = isNegative ? '-' : '';
        inlineEl.classList.remove('profit-final');
        inlineEl.classList.add('profit-rolling');
        valueEl.classList.add('value-visible');

        // Smooth scroll to bottom
        await (async ms => {
            const startY = window.scrollY;
            const distance = Math.max(document.documentElement.scrollHeight - window.innerHeight, 0) - startY;
            if (Math.abs(distance) < 6) return;
            const t0 = performance.now();
            await new Promise(resolve => {
                const step = now => {
                    const p = Math.min(1, (now - t0) / ms);
                    window.scrollTo(0, startY + distance * (p < 0.5 ? 2 * p * p : 1 - Math.pow(-2 * p + 2, 2) / 2));
                    p < 1 ? requestAnimationFrame(step) : resolve();
                };
                requestAnimationFrame(step);
            });
        })(scrollMs);

        inlineEl.scrollIntoView({ behavior: 'auto', block: 'center' });

        const rawDuration = durationMs ?? (1800 + 900 * Math.log10(Math.max(10, absValue + 1)));
        const clampedDuration = Math.max(minMs, Math.min(maxMs, rawDuration));

        // Rolling counter animation
        await (async (from, to, duration) => {
            const t0 = performance.now();
            return new Promise(resolve => {
                const step = now => {
                    const p = Math.min(1, (now - t0) / duration);
                    const eased = p * p * p;
                    const current = from + (to - from) * eased;
                    intEl.textContent = Math.floor(current).toLocaleString();
                    decEl.textContent = Math.floor(100 * (current - Math.floor(current))).toString().padStart(2, '0');
                    inlineEl.style.setProperty('--sniper-intensity', (p * p).toFixed(3));
                    p < 1 ? requestAnimationFrame(step) : resolve();
                };
                requestAnimationFrame(step);
            });
        })(0, absValue, clampedDuration);

        intEl.textContent = Math.floor(absValue).toLocaleString();
        decEl.textContent = Math.round(100 * (absValue - Math.floor(absValue))).toString().padStart(2, '0');

        inlineEl.classList.remove('profit-rolling');
        inlineEl.classList.add('profit-final');
        await new Promise(r => setTimeout(r, 300));
        return true;
    } catch (err) {
        console.error('Failed to insert profit inline:', err);
        return false;
    }
}


// ─────────────────────────────────────────────
// ETA message builders
// ─────────────────────────────────────────────

// Build ETA message from a custom user-defined prompt
async function buildCustomEtaMessage(orderDetails) {
    try {
        const { customEtaMessagePrompt } = await chrome.storage.local.get({ customEtaMessagePrompt: '' });
        const customerName = orderDetails?.customer?.name || '';
        const firstName = customerName ? customerName.split(' ')[0] : '';
        const itemName = orderDetails?.itemName ? String(orderDetails.itemName) : '';
        const etaText = orderDetails?.estimatedDeliveryDate ? String(orderDetails.estimatedDeliveryDate) : '';
        const prompt = String(customEtaMessagePrompt || '');

        const userMessage = JSON.stringify({ buyer_first_name: firstName, item_name: itemName, raw_eta_text: etaText });

        const result = await openaiLibrary.requestStructuredField({
            system: prompt,
            user: userMessage,
            name: 'eta_message',
            type: 'string',
            model: 'gpt-4o-mini',
        });

        return result?.ok
            ? { ok: true, data: (result.data || '').trim() }
            : { ok: false, error: result?.error || 'Failed to build custom ETA message' };
    } catch (err) {
        return { ok: false, error: err?.message || String(err) };
    }
}

// Determine the language from the eBay hostname / customer country
function detectLanguageCode(hostname, country) {
    if (hostname.includes('ebay.de')) return 'de';
    if (hostname.includes('ebay.fr')) return 'fr';
    if (hostname.includes('ebay.es')) return 'es';
    if (hostname.includes('ebay.it')) return 'it';
    if (hostname.includes('ebay.com')) return 'en';
    if (hostname.includes('ebay.ca')) return 'en';
    if (hostname.includes('ebay.co.uk')) return 'en';
    if (hostname.includes('ebay.com.au')) return 'en';

    if (country) {
        const c = country.toLowerCase();
        if (c.includes('germany') || c === 'de') return 'de';
        if (c.includes('france') || c === 'fr') return 'fr';
        if (c.includes('spain') || c === 'es') return 'es';
        if (c.includes('italy') || c === 'it') return 'it';
        if (c.includes('canada') || c === 'ca') return 'en';
        if (c.includes('united kingdom') || c === 'uk' || c === 'gb') return 'en';
        if (c.includes('australia') || c === 'au') return 'en';
        if (c.includes('united states') || c === 'us') return 'en';
    }
    return 'en';
}

// Build the default (non-custom) ETA message
async function buildDefaultEtaMessage(orderDetails) {
    try {
        const customerName = orderDetails?.customer?.name || '';
        const firstName = customerName ? customerName.split(' ')[0] : 'there';
        const itemName = orderDetails?.itemName ? String(orderDetails.itemName) : '';
        const etaText = orderDetails?.estimatedDeliveryDate ? String(orderDetails.estimatedDeliveryDate) : '';
        const langCode = detectLanguageCode(
            window.location.hostname || '',
            orderDetails?.customer?.address?.country || ''
        );

        const userMessage = [
            `language_code: ${langCode}`,
            `buyer_first_name: ${firstName}`,
            `item_name: ${itemName}`,
            `raw_eta_text: ${etaText}`,
            'notes:',
            "- raw_eta_text can be a date or a phrase like 'Arriving today' or 'Arriving tomorrow'.",
            '- raw_eta_text may sometimes be empty or missing, or \'payment verification\' messages; in that case, create a generic friendly update without ETA.',
            '- Do not include sender name or store name.',
            '- Keep the tone friendly and brief.',
        ].join('\n');

        const systemPrompt = `You are writing a short, casual, friendly order update for an eBay buyer.

      Tone rules:
      - Write like you're texting a friend, not a customer. Warm, human, and easygoing.
      - Always thank them for supporting a small family business.
      - Never apologize, never say sorry. We elevate ourselves, not lower status.
      - Mention their first name if provided, otherwise use a friendly greeting.
      - Simplify the product name so it sounds natural. Do not repeat long or clunky catalog titles.
      - Never mention payment, verification, delays, or issues — only positive updates.
      - State clearly when they can expect the order, using the provided ETA text, but keep it conversational 
        (e.g., "Your treadmill should be at your door tomorrow").
      - Keep it short, cheerful, and memorable. 
      - No emojis, no signatures, no store names.

      Your only output is the exact message text to send.
        `;

        const result = await openaiLibrary.requestStructuredField({
            system: systemPrompt,
            user: userMessage,
            name: 'eta_message',
            type: 'string',
            model: 'gpt-4o-mini',
        });

        return result?.ok
            ? { ok: true, data: result.data }
            : { ok: false, error: result?.error || 'Failed to build ETA message' };
    } catch (err) {
        return { ok: false, error: err?.message || String(err) };
    }
}


// ─────────────────────────────────────────────
// Note builders
// ─────────────────────────────────────────────

// Build the default eBay note
async function buildDefaultNote(orderDetails) {
    const { agentName } = await chrome.storage.local.get('agentName');
    const today = new Date().toLocaleDateString();
    let note = 'SS';
    note += ' ' + orderDetails.amazonEmail;
    note += ' - ETA: ' + orderDetails.estimatedDeliveryDate;
    note += ' - ' + today + ' ' + agentName;
    const existing = getExistingNote();
    if (existing) note = existing + ' - ' + note;
    return note;
}

// Build a note from the user's custom template (supports {{tokens}})
async function buildCustomNoteFromTemplate(orderDetails) {
    const { customNoteTemplate = '' } = await chrome.storage.local.get({ customNoteTemplate: '' });
    const { agentName = '' } = await chrome.storage.local.get({ agentName: '' });

    let template = (customNoteTemplate || 'SS {{amazonEmail}} - #{{amazonOrderNumber}} - ETA: {{eta}} - {{date}} {{agent}}').trim();

    const tokens = {
        agent: agentName,
        agentname: agentName,
        date: new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }),
        ebayordernumber: orderDetails.ebayOrderNumber || '',
        amazonordernumber: orderDetails.amazonOrderNumber || '',
        itemname: orderDetails.itemName || '',
        quantitysold: orderDetails.quantitySold || '',
        customername: orderDetails.customer?.name || '',
        customerfirstname: (orderDetails.customer?.name || '').split(' ')[0] || '',
        customeraddress: orderDetails.customerAddress || '',
        customercity: orderDetails.customer?.address?.city || '',
        customerstate: orderDetails.customer?.address?.state || '',
        customerzip: orderDetails.customer?.address?.zip || '',
        customercountry: orderDetails.customer?.address?.country || '',
        customerphone: orderDetails.customer?.phone || '',
        eta: orderDetails.estimatedDeliveryDate || '',
        estimateddeliverydate: orderDetails.estimatedDeliveryDate || '',
        fulfillmentdate: orderDetails.fulfillmentDate || '',
        trackingnumber: orderDetails.trackingNumber || '',
        soldprice: orderDetails.soldPrice || '',
        grandtotal: orderDetails.grandTotal || '',
        orderearnings: orderDetails.orderEarnings || '',
        profit: orderDetails.profit || '',
        orderearningscurrency: orderDetails.orderEarningsCurrency || '',
        grandtotalcurrency: orderDetails.grandTotalCurrency || '',
        domain: orderDetails.domain || '',
        ebayusername: orderDetails.ebayUsername || '',
        amazonemail: orderDetails.amazonEmail || '',
        amazondomain: orderDetails.amazonDomain || '',
    };

    template = template.replace(/\{\{\s*([^}]+)\s*\}\}/gi, (_, key) => {
        const lower = key.trim().toLowerCase();
        return tokens[lower] !== undefined ? String(tokens[lower]) : '';
    });

    const existing = typeof getExistingNote === 'function' && getExistingNote() || '';
    if (existing) template = existing + ' - ' + template;
    return template.trim();
}


// ─────────────────────────────────────────────
// Amazon-order polling
// ─────────────────────────────────────────────

async function pollForAmazonOrder(ebayOrderNumber, timeoutMs = 300000, intervalMs = 5000) {
    const sleep = ms => new Promise(r => setTimeout(r, ms));
    const url = `https://us-central1-silentsnipe-ce4ca.cloudfunctions.net/app/api/v1/ebayOrders/check/${ebayOrderNumber}`;
    const deadline = Date.now() + timeoutMs;

    while (Date.now() < deadline) {
        console.log('polling for Amazon order');
        const res = await fetch(url, {
            method: 'GET',
            headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
        });

        if (!res.ok) return { error: `HTTP ${res.status}` };

        const json = await res.json();
        console.log('Amazon order lookup response:', json);

        if (json?.data?.remarks?.includes('ERROR')) return { error: json.data.remarks };
        if (json?.data?.amazonOrderNumber || json?.data?.amazonOrderDetails) {
            console.log('Polling for Amazon order was successful');
            return json.data;
        }

        await sleep(intervalMs);
    }

    return { error: "Couldn't find the supplier order after checking for a while." };
}


// ─────────────────────────────────────────────
// Mark as shipped
// ─────────────────────────────────────────────

async function markOrderAsShipped() {
    try {
        const link = document.querySelector('[data-action-id="MARK_SHIPPED"] a');
        if (!link) return false;
        const href = link.getAttribute('href');
        if (!href) return false;

        const res = await fetch(href, { method: 'GET', credentials: 'include' });
        const text = await res.text();

        let parsed;
        try { parsed = JSON.parse(text); } catch { parsed = null; }

        if (parsed && parsed.ack === 'SUCCESS') return true;
        if (typeof text === 'string' && text.includes('Update Shipment operation success')) return true;
        return false;
    } catch (err) {
        console.error('❌ Error marking as shipped:', err);
        return false;
    }
}

// Inject the "Marked as Shipped" banner UI
async function showShippedBanner() {
    if (!document.getElementById('sniper-power-css')) {
        const style = document.createElement('style');
        style.id = 'sniper-power-css';
        style.textContent = `
:root {
  --sniper-ink:#0b0f13;
  --sniper-sub:#5b626a;
  --sniper-card:#ffffff;
  --sniper-border:rgba(0,0,0,.08);
  --sniper-green:#16a34a;
  --sniper-green-rgb:22,163,74;
  --sniper-steel-1:#eef1f4;
  --sniper-steel-2:#dfe5ea;
}
.sniper-power-host { position:relative !important; overflow:hidden !important; border-radius:12px !important; }
.sniper-mask-all > *:not(.sniper-power-replace) { visibility:hidden !important; pointer-events:none !important; }
.sniper-power-replace {
  position:absolute; inset:-1px; border-radius:inherit; z-index:5; pointer-events:auto;
  background: var(--sniper-card);
  box-shadow: inset 0 0 0 1px var(--sniper-border);
  display:grid; grid-template-rows:auto 1fr; opacity:0;
}
.sniper-power-replace::before {
  content:""; position:absolute; inset:-2px; border-radius:inherit; pointer-events:none;
  background: linear-gradient(135deg, rgba(var(--sniper-green-rgb),.45), rgba(0,0,0,.0) 35%, rgba(0,110,255,.35) 65%, rgba(0,0,0,.0));
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  padding:2px; -webkit-mask-composite: xor; mask-composite: exclude;
  filter: blur(.2px); opacity:.85;
}
.sniper-power-head {
  position:relative; display:grid; grid-template-columns:auto 1fr; align-items:center; column-gap:14px;
  padding:18px;
  background: linear-gradient(180deg, #fff, #fafbfc);
  border-bottom:1px solid var(--sniper-border);
}
.sniper-power-head::after {
  content:""; position:absolute; left:0; right:0; top:0; height:3px; pointer-events:none;
  background: linear-gradient(90deg, rgba(var(--sniper-green-rgb),.0) 0%, rgba(var(--sniper-green-rgb),.85) 20%, rgba(var(--sniper-green-rgb),.0) 100%);
  transform-origin:left; transform: scaleX(0);
}
.sniper-power-body { padding:16px 18px 18px; display:flex; flex-wrap:wrap; gap:10px; align-content:start; }
.sniper-power-crest {
  position:relative; width:46px; height:46px; display:grid; place-items:center;
}
.sniper-power-crest::before {
  content:""; position:absolute; inset:-6px; border-radius:999px;
  background: radial-gradient(circle at 50% 50%, rgba(var(--sniper-green-rgb),.35), rgba(var(--sniper-green-rgb),0) 60%);
  transform: scale(.85); opacity:0;
}
.sniper-power-crest::after {
  content:""; position:absolute; inset:0; border-radius:999px;
  background: conic-gradient(from 0deg, rgba(var(--sniper-green-rgb),.0), rgba(var(--sniper-green-rgb),.6), rgba(var(--sniper-green-rgb),.0) 70%);
  filter: blur(10px); opacity:0; transform: rotate(-12deg);
}
.sniper-power-crest .tick {
  width:34px; height:34px; border-radius:999px; display:grid; place-items:center;
  background: var(--sniper-green); color:#fff; font-weight:900; box-shadow:0 6px 16px rgba(var(--sniper-green-rgb),.35);
}
.sniper-power-title { font:800 18px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif; color:var(--sniper-ink); }
.sniper-power-sub   { font:600 12px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif; color:var(--sniper-sub); margin-top:2px; }
.sniper-chip {
  display:inline-flex; align-items:center; gap:8px; padding:8px 12px; border-radius:999px;
  font:700 12px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif; color:var(--sniper-ink);
  background: linear-gradient(180deg, var(--sniper-steel-1), var(--sniper-steel-2));
  border:1px solid rgba(0,0,0,.08);
}
.sniper-chip--ok {
  background: linear-gradient(180deg, rgba(var(--sniper-green-rgb),.14), rgba(var(--sniper-green-rgb),.10));
  border-color: rgba(var(--sniper-green-rgb),.35);
}
.sniper-power-grid {
  position:absolute; inset:0; pointer-events:none; opacity:.045;
  background:
    repeating-linear-gradient(0deg, #000 0 1px, transparent 1px 24px),
    repeating-linear-gradient(90deg, #000 0 1px, transparent 1px 24px);
}
.sniper-armed .sniper-power-replace { animation: power-fade .18s ease-out forwards; }
.sniper-armed .sniper-power-head::after { animation: power-sweep .7s cubic-bezier(.2,.8,.2,1) .05s forwards; }
.sniper-armed .sniper-power-crest::before { animation: halo-pulse .7s ease-out .06s forwards; }
.sniper-armed .sniper-power-crest::after  { animation: halo-arc 1.1s ease-out .08s forwards; }
@keyframes power-fade { from{opacity:0} to{opacity:1} }
@keyframes power-sweep { 0%{transform:scaleX(0)} 60%{transform:scaleX(1.06)} 100%{transform:scaleX(1)} }
@keyframes halo-pulse { 0%{opacity:0;transform:scale(.85)} 60%{opacity:1;transform:scale(1.05)} 100%{opacity:.85;transform:scale(1)} }
@keyframes halo-arc   { 0%{opacity:0;transform:rotate(-12deg)} 100%{opacity:.85;transform:rotate(0deg)} }
@media (prefers-reduced-motion:reduce) {
  .sniper-armed .sniper-power-replace,
  .sniper-armed .sniper-power-head::after,
  .sniper-armed .sniper-power-crest::before,
  .sniper-armed .sniper-power-crest::after { animation:none !important; opacity:1 !important; transform:none !important; }
}
    `;
        document.head.appendChild(style);
    }

    const actionEl =
        document.querySelector('[data-action-id="MARK_SHIPPED"]') ||
        document.querySelector('[data-action-id="ADD_TRACKING_NUMBER"]') ||
        document.querySelector('[data-action-id]');
    if (!actionEl) return false;

    const widget =
        actionEl.closest('.status-summary.widget') ||
        actionEl.closest('.status-summary') ||
        actionEl.closest('[class*="status-summary"]') ||
        actionEl.closest('.widget');
    if (!widget) return false;

    widget.classList.add('sniper-power-host', 'sniper-mask-all');
    widget.querySelectorAll('[data-action-id="MARK_SHIPPED"]').forEach(el => (el.style.display = 'none'));
    widget.querySelector('.sniper-power-replace')?.remove();

    const banner = document.createElement('div');
    banner.className = 'sniper-power-replace';
    banner.innerHTML = `
    <div class="sniper-power-grid" aria-hidden="true"></div>
    <div class="sniper-power-head">
      <div class="sniper-power-crest"><div class="tick">✔</div></div>
      <div>
        <div class="sniper-power-title">Marked as Shipped</div>
        <div class="sniper-power-sub">Updated by SilentSniper</div>
      </div>
    </div>
    <div class="sniper-power-body">
      <span class="sniper-chip sniper-chip--ok">Status: Shipped</span>
      <span class="sniper-chip">You can add tracking anytime</span>
    </div>
  `;
    widget.appendChild(banner);
    window.scrollTo(0, 0);

    await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
    widget.scrollIntoView({ behavior: 'auto', block: 'center' });
    document.documentElement.classList.add('sniper-armed');
    await new Promise(r => setTimeout(r, 800));
    return true;
}


// ─────────────────────────────────────────────
// Send ETA message to eBay buyer via in-page messaging
// ─────────────────────────────────────────────

async function sendEtaToEbayBuyer(message) {
    chrome.runtime.sendMessage({ type: 'makeTabActive', shouldSwitchBack: true });

    function findMessageButton() {
        return document.querySelector('.order-info .actions .btn--secondary');
    }

    function closeMessageDialog() {
        const closeBtn = document.querySelector('.panel-dialog__close');
        if (closeBtn) closeBtn.click();
    }

    for (let attempt = 1; attempt <= 4; attempt++) {
        try {
            const msgBtn = findMessageButton();
            if (!msgBtn) throw new Error("Unable to find 'Message Buyer' button");

            msgBtn.scrollIntoView({ block: 'center' });
            msgBtn.click();

            const iframe = await iframeUtils.parent.waitForIframeReady('.ordui-m2m-panel__iframe', 20000);
            const reply = await iframeUtils.parent.askIframe(iframe, 'send_message_to_buyer', { message }, 20000);
            console.log('Iframe reply:', reply);
            closeMessageDialog();
            return true;
        } catch (err) {
            console.warn(`sendEbayBuyerETA attempt ${attempt} failed:`, err?.message ?? err);
            if (attempt === 4) throw err;
            closeMessageDialog();
            await new Promise(r => setTimeout(r, 5000));
        }
    }
}


// ─────────────────────────────────────────────
// Profit calculation
// ─────────────────────────────────────────────

function calculateProfit(earnings, cost) {
    function parse(val) {
        if (val == null) return 0;
        let s = String(val).trim();
        // Handle European number format (e.g. "1.234,56")
        if (s.includes(',') && /,\d{1,2}$/.test(s)) {
            s = s.replace(/\./g, '').replace(/,/g, '.');
        }
        s = s.replace(/[^0-9.-]/g, '');
        return parseFloat(s) || 0;
    }
    return +(parse(earnings) - parse(cost)).toFixed(2);
}

// Convert an amount between two currencies (static rates)
function convertCurrency(amount, fromCurrency, toCurrency) {
    const rates = {
        CAD_TO_USD: 0.727,
        USD_TO_CAD: 1 / 0.727,
        EUR_TO_USD: 1.1712,
        USD_TO_EUR: 1 / 1.1712,
        GBP_TO_USD: 1.3,
        USD_TO_GBP: 1 / 1.3,
    };
    const key = `${fromCurrency.trim().toUpperCase()}_TO_${toCurrency.trim().toUpperCase()}`;
    if (!rates[key]) {
        console.warn(`No conversion rate available for ${fromCurrency} to ${toCurrency}. Returning original amount.`);
        return Number(amount.toFixed(2));
    }
    return Number((amount * rates[key]).toFixed(2));
}

// Returns true if the two currency codes are different (i.e. conversion is needed)
function needsCurrencyConversion(currency1, currency2) {
    const a = (currency1 || '').trim().toUpperCase();
    const b = (currency2 || '').trim().toUpperCase();
    return !!(a && b && a !== b);
}

// Wait for a button to fire a named custom event
function waitForButtonEvent(btn, eventName = 'increase') {
    return new Promise(resolve => {
        btn.addEventListener(`${eventName}:done`, e => resolve(e.detail), { once: true });
        btn.click();
    });
}


// ─────────────────────────────────────────────
// Options-page initialisation (DOMContentLoaded)
// ─────────────────────────────────────────────

document.addEventListener('DOMContentLoaded', () => {
    bindCheckbox('is-international', 'isInternational', false);
    bindCheckbox('doesUserHaveEbaySubscription', 'doesUserHaveEbaySubscription', true);
    bindCheckbox('isUserTaxExempt', 'isUserTaxExempt', false);
    bindCheckbox('forceItemLocationSwitch', 'forceItemLocationSwitch', false);
    bindText('itemLocation', 'itemLocation', '');
    bindSelect('itemLocationCountry', 'itemLocationCountry', '');
    bindText('itemLocationCityState', 'itemLocationCityState', '');
    bindCheckbox('forceReturnPolicySwitch', 'forceReturnPolicySwitch', false);
    bindText('returnPolicyId', 'returnPolicyId', '');
    bindCheckbox('disableThankYouMessage', 'disableThankYouMessage', false);
    bindCheckbox('onlyListOnePicture', 'onlyListOnePicture', false);
    bindCheckbox('removeWordsFromListingSwitch', 'removeWordsFromListingSwitch', false);
    bindCheckbox('useCustomDescriptionPrompt', 'useCustomDescriptionPrompt', false);
    bindText('descriptionPrompt', 'descriptionPrompt',
        "You are an expert copywriter. Turn the user's text into an appealing product description. Keep it concise and professional.");
    bindCheckbox('useCustomDescriptionTemplate', 'useCustomDescriptionTemplate', false);

    document.getElementById('openDescriptionTemplateBuilder').addEventListener('click', () => {
        const url = chrome.runtime.getURL('../description_template_builder/index.html');
        window.open(url, '_blank');
    });

    bindCheckbox('useCustomTitleGenerator', 'useCustomTitleGenerator', false);
    bindText('custom_title_prompt', 'custom_title_prompt',
        'You are an expert copywriter. Create a catchy title for the product based on the description. Keep it concise and professional.');
    bindCheckbox('removeSectionsFromDescriptionTemplate', 'removeSectionsFromDescriptionTemplate', false);
    bindCheckbox('useAIGeneratedImage', 'useAIGeneratedImage', false);
    bindCheckbox('useCustomImagePrompt', 'useCustomImagePrompt', false);
    bindText('geminiApiKey', 'geminiApiKey', '');
    bindText('custom_image_prompt', 'custom_image_prompt',
        `You will receive only a product title.  
Your job is to write ONE short instruction (max 1–2 sentences) as if you are briefing a professional image editor.

The instruction must:  
- Make the product the main subject, filling about 70% of the frame.  
- Keep the product sharp and clear; background soft and slightly blurred.  
- Suggest a realistic lifestyle setting that matches the product's use (living room, office, bedroom, foyer, etc.).  
- Include lighting and atmosphere (e.g. warm natural light, cozy, modern, elegant).  
- Be concise, visual, and natural. Do not restate product specs.  
- Output only the instruction text.
`);

    bindCheckbox('useUniversalPromptForAllImages', 'useUniversalPromptForAllImages', false);
    bindText('universalImagePrompt', 'universalImagePrompt',
        'Make this look like a real customer review photo on Amazon or a Facebook Marketplace listing');
    bindCheckbox('autoDisplayAddress', 'autoDisplayAddress', false);
    bindText('amazonEmail', 'amazonEmail', '');
    bindCheckbox('useCustomEtaMessagePrompt', 'useCustomEtaMessagePrompt', false);
    bindText('customEtaMessagePrompt', 'customEtaMessagePrompt',
        `You are writing a short, casual, friendly order update for an eBay buyer.

        Tone rules:
        - Write like you're texting a friend, not a customer. Warm, human, and easygoing.
        - Always thank them for supporting a small family business.
        - Never apologize, never say sorry. We elevate ourselves, not lower status.
        - Mention their first name if provided, otherwise use a friendly greeting.
        - Simplify the product name so it sounds natural. Do not repeat long or clunky catalog titles.
        - State clearly when they can expect the order, using the provided ETA text, but keep it conversational (e.g., "Your treadmill should be at your door tomorrow").
        - Keep it short, cheerful, and memorable. No emojis, no signatures, no store names.

        Your only output is the exact message text to send.`);

    bindCheckbox('useCustomNoteTemplate', 'useCustomNoteTemplate', false);
    bindText('customNoteTemplate', 'customNoteTemplate',
        'SS {{amazonEmail}} - #{{amazonOrderNumber}} - ETA: {{eta}} - {{date}} {{agent}}');
    bindCheckbox('useCustomAmazonPhoneNumber', 'useCustomAmazonPhoneNumber', false);
    bindText('customAmazonPhoneNumber', 'customAmazonPhoneNumber', '');
    bindCheckbox('useVATOnListing', 'useVATOnListing', false);
    bindText('customVATPercentage', 'customVATPercentage', '19');
    bindSelect('whichAmazonAddressToEdit', 'whichAmazonAddressToEdit', '4');

    // Default German blacklist words
    const defaultWordsToRemove = [
        'Test Sieger', 'Nr1', 'Garantie', 'CE', 'Zertifikat', 'Original',
        'Beste Qualität', 'Nr. 1', 'Marktführer', 'Unschlagbar', 'Weltneuheit',
        'Premium', 'Garantiert sicher', 'Zertifizierungen', 'Prüfzeichen',
        'CE geprüft', 'TÜV-geprüft', 'Zertifiziert', 'Geprüfte Qualität',
        'ISO-zertifiziert', 'GS geprüft',
    ].join('\n');

    bindText('wordsToRemove', 'wordsToRemove', defaultWordsToRemove);

    document.getElementById('wordsToRemove').addEventListener('input', function () {
        chrome.storage.local.set({ wordsToRemove: this.value });
    });
    document.getElementById('resetWords').addEventListener('click', () => {
        document.getElementById('wordsToRemove').value = defaultWordsToRemove;
        chrome.storage.local.set({ wordsToRemove: defaultWordsToRemove });
    });

    bindCheckbox('monitor-switch', 'monitorGmail', false);

    document.getElementById('sign-in-button').addEventListener('click', () => {
        chrome.identity.getAuthToken({ interactive: true }, token => {
            if (chrome.runtime.lastError) {
                alert('Failed to authenticate: ' + chrome.runtime.lastError.message);
            } else {
                chrome.storage.local.set({ accessToken: token }, () => {
                    alert('Successfully authenticated with Gmail!');
                });
            }
        });
    });

    document.getElementById('save').addEventListener('click', () => alert('Settings saved'));
    bindText('maxExtendedDeliveryDays', 'maxExtendedDeliveryDays', 15);

    initBlacklistUI();
});


// ─────────────────────────────────────────────
// Settings binding helpers
// ─────────────────────────────────────────────

function bindCheckbox(elementId, storageKey, defaultValue) {
    const el = document.getElementById(elementId);
    chrome.storage.local.get(storageKey, result => {
        const value = result[storageKey] !== undefined ? result[storageKey] : defaultValue;
        el.checked = value;
        if (result[storageKey] === undefined) chrome.storage.local.set({ [storageKey]: defaultValue });
    });
    el.addEventListener('change', () => {
        chrome.storage.local.set({ [storageKey]: el.checked });
    });
}

function bindText(elementId, storageKey, defaultValue) {
    const el = document.getElementById(elementId);
    chrome.storage.local.get(storageKey, result => {
        const value = result[storageKey] !== undefined ? result[storageKey] : defaultValue;
        el.value = value;
        if (result[storageKey] === undefined) chrome.storage.local.set({ [storageKey]: defaultValue });
    });
    el.addEventListener('change', () => {
        chrome.storage.local.set({ [storageKey]: el.value });
    });
}

function bindSelect(elementId, storageKey, defaultValue) {
    const el = document.getElementById(elementId);
    chrome.storage.local.get(storageKey, result => {
        const value = result[storageKey] !== undefined ? result[storageKey] : defaultValue;
        el.value = value;
        if (result[storageKey] === undefined) chrome.storage.local.set({ [storageKey]: defaultValue });
    });
    el.addEventListener('change', () => {
        chrome.storage.local.set({ [storageKey]: el.value });
    });
}


// ─────────────────────────────────────────────
// ETA-message test button (options page)
// ─────────────────────────────────────────────

document.getElementById('testEtaMessageButton').addEventListener('click', async () => {
    const btn = document.getElementById('testEtaMessageButton');
    btn.disabled = true;
    btn.textContent = 'Loading...';
    btn.style.backgroundColor = '#ccc';

    const resultEl = document.getElementById('testEtaMessageResult');

    const result = await buildCustomEtaMessage({
        itemName: 'Lightweight LED Head Magnifier with Multiple Lens Options for Precision Work',
        estimatedDeliveryDate: 'Tomorrow',
        customer: { name: 'Jennifer Mier' },
    });

    if (result.ok) {
        resultEl.textContent = result.data;
        btn.disabled = false;
        btn.textContent = 'Generate for Jennifer – LED Magnifier (Tomorrow)';
        btn.style.backgroundColor = '';
    } else {
        resultEl.textContent = 'Error: ' + result.error;
    }
});


// ─────────────────────────────────────────────
// Blacklist UI (options page)
// ─────────────────────────────────────────────

async function initBlacklistUI() {
    const radioAuto = document.getElementById('blkSourceAuto');
    const radioCustom = document.getElementById('blkSourceCustom');
    const metaEl = document.getElementById('blacklistMeta');
    const helperEl = document.getElementById('blacklistHelper');
    const viewerEl = document.getElementById('blacklistViewer');
    const previewEl = document.getElementById('ecomPreview');
    const updateBtn = document.getElementById('updateBlacklistButton');
    const previewWrap = document.getElementById('ecomPreviewWrap');
    const myListWrap = document.getElementById('myListWrap');

    function formatMeta({ updated, count, using, region }) {
        return (
            `Using: ${using === 'custom' ? 'My list' : 'SilentSniper list'}` +
            ` · Updated: ${updated ? new Date(updated).toLocaleString() : 'Never'}` +
            ` · Words: ${Number.isFinite(count) ? count : 0}` +
            (region ? ` · Region: ${region}` : '')
        );
    }

    async function refreshUI() {
        const {
            blacklist_last_updated,
            blacklist,
            blacklist_auto,
            blacklist_custom_enabled,
            blacklist_custom,
            domain,
        } = await chrome.storage.local.get([
            'blacklist_last_updated', 'blacklist', 'blacklist_auto',
            'blacklist_custom_enabled', 'blacklist_custom', 'domain',
        ]);

        const useCustom = !!blacklist_custom_enabled;
        radioCustom.checked = useCustom;
        radioAuto.checked = !useCustom;

        const activeList = Array.isArray(blacklist) ? blacklist : [];
        metaEl.textContent = formatMeta({
            updated: blacklist_last_updated,
            count: activeList.length,
            using: useCustom ? 'custom' : 'auto',
            region: domain || 'com',
        });

        helperEl.textContent = (() => {
            if (useCustom) return 'Your list wins. We never change your list.';
            if (domain !== 'de') return 'SilentSniper list works only for Germany (de). In other regions this list stays empty.';
            return 'Pulls the latest SilentSniper blacklist for Germany.';
        })();

        const autoList = Array.isArray(blacklist_auto) ? blacklist_auto : [];
        previewEl.value = autoList.join('\n');
        previewWrap.style.display = useCustom ? 'none' : '';
        myListWrap.style.display = useCustom ? '' : 'none';

        if (useCustom) {
            viewerEl.value = Array.isArray(blacklist_custom) ? blacklist_custom.join('\n') : '';
            viewerEl.disabled = false;
            viewerEl.style.background = '';
        } else {
            viewerEl.value = '';
            viewerEl.placeholder = 'Switch to "Use my list" to type your words (one per line)';
            viewerEl.disabled = true;
            viewerEl.style.background = '#f5f5f5';
        }
    }

    // Switch to auto list
    radioAuto.addEventListener('change', async () => {
        if (!radioAuto.checked) return;
        const { blacklist_auto } = await chrome.storage.local.get('blacklist_auto');
        const list = Array.isArray(blacklist_auto) ? blacklist_auto : [];
        await chrome.storage.local.set({ blacklist_custom_enabled: false, blacklist: list });
        await refreshUI();
    });

    // Switch to custom list
    radioCustom.addEventListener('change', async () => {
        if (!radioCustom.checked) return;
        const { blacklist_custom, blacklist_auto } = await chrome.storage.local.get(['blacklist_custom', 'blacklist_auto']);
        const customList = Array.isArray(blacklist_custom) ? blacklist_custom : [];
        const autoList = Array.isArray(blacklist_auto) ? blacklist_auto : [];
        const toUse = customList.length ? customList : autoList;
        await chrome.storage.local.set({ blacklist_custom_enabled: true, blacklist_custom: toUse, blacklist: toUse });
        await refreshUI();
    });

    // Live edit of custom list (debounced)
    let debounceTimer = null;
    viewerEl.addEventListener('input', () => {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(async () => {
            const words = Array.from(new Set(
                viewerEl.value.split(/\r?\n/g).map(w => w.trim()).filter(Boolean).map(w => w.toLowerCase())
            ));
            const { blacklist_custom_enabled } = await chrome.storage.local.get('blacklist_custom_enabled');
            if (!blacklist_custom_enabled) return;
            await chrome.storage.local.set({ blacklist_custom: words, blacklist: words });
            const { blacklist_last_updated } = await chrome.storage.local.get('blacklist_last_updated');
            metaEl.textContent = formatMeta({ updated: blacklist_last_updated, count: words.length, using: 'custom' });
        }, 400);
    });

    // Manual "Update blacklist" button
    updateBtn.addEventListener('click', async () => {
        updateBtn.disabled = true;
        const originalLabel = updateBtn.textContent;
        updateBtn.textContent = 'Updating…';

        const { domain, blacklist_custom_enabled, blacklist_custom } =
            await chrome.storage.local.get(['domain', 'blacklist_custom_enabled', 'blacklist_custom']);

        if (domain !== 'de') {
            const useCustom = !!blacklist_custom_enabled;
            const customList = Array.isArray(blacklist_custom) ? blacklist_custom : [];
            const now = new Date().toISOString();
            await chrome.storage.local.set({
                blacklist_auto: [],
                blacklist_last_updated: now,
                ...(useCustom ? { blacklist: customList } : { blacklist: [] }),
            });
        } else {
            await updateBlacklistFromServer();
        }

        updateBtn.disabled = false;
        updateBtn.textContent = originalLabel;
        await refreshUI();
    });

    await refreshUI();
}



