// QuickBooks Online API service
const https = require('https');
const { retryNetworkOperation, makeHttpsRequest } = require('../utils/network');
const { auditLog } = require('../utils/logging');

class QuickBooksOnlineService {
    constructor(accessToken, realmId, environment = 'sandbox') {
        this.accessToken = accessToken;
        this.realmId = realmId;
        this.environment = environment;
        this.baseUrl = environment === 'production' 
            ? 'https://quickbooks.api.intuit.com' 
            : 'https://sandbox-quickbooks.api.intuit.com';
    }

    // Helper function to make QuickBooks Online API requests
    async makeQBOApiRequest(endpoint, method = 'GET', data = null) {
        if (!this.accessToken || !this.realmId) {
            throw new Error('QuickBooks not connected - missing access token or realm ID');
        }

        const url = `${this.baseUrl}/v3/company/${this.realmId}/${endpoint}`;
        
        console.error('QBO_API_REQUEST: Making request to URL:', url);
        console.error('QBO_API_REQUEST: Method:', method);
        console.error('QBO_API_REQUEST: Environment:', this.environment);
        console.error('QBO_API_REQUEST: RealmId:', this.realmId);
        
        const options = {
            method: method,
            headers: {
                'Authorization': `Bearer ${this.accessToken}`,
                'Accept': 'application/json',
                'Content-Type': 'application/json',
                'User-Agent': 'QuickBooks-Payment-Gateway/1.0'
            }
        };

        let postData = null;
        if (data && (method === 'POST' || method === 'PUT')) {
            postData = JSON.stringify(data);
            options.headers['Content-Length'] = Buffer.byteLength(postData);
        }

        return await retryNetworkOperation(
            () => makeHttpsRequest(url, options, postData),
            `QBO_API_${method}_${endpoint}`
        );
    }

    // Search for existing customer by email
    async findCustomerByEmail(email) {
        try {
            console.error('CUSTOMER_SEARCH: Searching for customer with email:', email);
            
            const encodedEmail = encodeURIComponent(email);
            const query = `SELECT * FROM Customer WHERE PrimaryEmailAddr = '${encodedEmail}'`;
            const encodedQuery = encodeURIComponent(query);
            
            const response = await this.makeQBOApiRequest(`query?query=${encodedQuery}`);
            
            console.error('CUSTOMER_SEARCH: Response status:', response.status);
            console.error('CUSTOMER_SEARCH: Response data:', JSON.stringify(response.data, null, 2));
            
            if (response.data && response.data.QueryResponse && response.data.QueryResponse.Customer) {
                const customers = Array.isArray(response.data.QueryResponse.Customer) 
                    ? response.data.QueryResponse.Customer 
                    : [response.data.QueryResponse.Customer];
                
                console.error('CUSTOMER_SEARCH: Found customers:', customers.length);
                return customers[0]; // Return first match
            }
            
            console.error('CUSTOMER_SEARCH: No customer found with email:', email);
            return null;
        } catch (error) {
            console.error('CUSTOMER_SEARCH: Error searching for customer:', error.message);
            return null;
        }
    }


    // Create customer from structured data (based on server.js.old working logic)
    async createCustomerFromData(customerData) {
        try {
            console.error('CUSTOMER_CREATE: Creating customer with data:', JSON.stringify(customerData, null, 2));
            
            const qboCustomerData = {
                DisplayName: customerData.displayName,
                PrimaryEmailAddr: customerData.email ? { Address: customerData.email } : undefined,
                PrimaryPhone: customerData.phone ? { FreeFormNumber: customerData.phone } : undefined,
                CompanyName: customerData.companyName || undefined,
                GivenName: customerData.firstName || undefined,
                FamilyName: customerData.lastName || undefined,
                BillAddr: customerData.billingAddress ? {
                    Line1: customerData.billingAddress.line1,
                    Line2: customerData.billingAddress.line2,
                    City: customerData.billingAddress.city,
                    CountrySubDivisionCode: customerData.billingAddress.state,
                    PostalCode: customerData.billingAddress.postalCode,
                    Country: customerData.billingAddress.country || 'US'
                } : undefined
            };

            // Remove undefined fields
            Object.keys(qboCustomerData).forEach(key => {
                if (qboCustomerData[key] === undefined) {
                    delete qboCustomerData[key];
                }
            });

            console.error('CUSTOMER_CREATE: Sending customer data to QuickBooks:', JSON.stringify(qboCustomerData, null, 2));
            
            const response = await this.makeQBOApiRequest('customer', 'POST', qboCustomerData);
            
            console.error('CUSTOMER_CREATE: Response status:', response.status);
            console.error('CUSTOMER_CREATE: Response data:', JSON.stringify(response.data, null, 2));
            
            // Check if QuickBooks returned an error
            if (response.data && response.data.Fault && response.data.Fault.Error) {
                const qbError = response.data.Fault.Error[0];
                const errorMessage = qbError.Message || qbError.Detail || 'QuickBooks API error';
                throw new Error(errorMessage);
            }
            
            if (response.data && response.data.Customer) {
                return response.data.Customer;
            }
            
            return null;
        } catch (error) {
            console.error('CUSTOMER_CREATE: Error creating customer:', error.message);
            throw error;
        }
    }

    // Find customer by display name
    async findCustomerByName(displayName) {
        try {
            console.error('CUSTOMER_SEARCH_NAME: Searching for customer with name:', displayName);
            
            const query = `SELECT * FROM Customer WHERE DisplayName = '${displayName}'`;
            const response = await this.makeQBOApiRequest(`query?query=${encodeURIComponent(query)}`, 'GET');
            
            console.error('CUSTOMER_SEARCH_NAME: Response status:', response.status);
            console.error('CUSTOMER_SEARCH_NAME: Response data:', JSON.stringify(response.data, null, 2));
            
            if (response.data && response.data.QueryResponse && response.data.QueryResponse.Customer) {
                const customers = response.data.QueryResponse.Customer;
                console.error('CUSTOMER_SEARCH_NAME: Found customers:', customers.length);
                return customers[0]; // Return first match
            }
            
            console.error('CUSTOMER_SEARCH_NAME: No customers found');
            return null;
        } catch (error) {
            console.error('CUSTOMER_SEARCH_NAME: Error searching for customer by name:', error.message);
            return null;
        }
    }

    // Find customer by display name AND verify email matches
    async findCustomerByName(displayName) {
        try {
            console.error('CUSTOMER_SEARCH_NAME: Searching for customer with name:', displayName);
            
            const query = `SELECT * FROM Customer WHERE DisplayName = '${displayName}'`;
            const response = await this.makeQBOApiRequest(`query?query=${encodeURIComponent(query)}`, 'GET');
            
            console.error('CUSTOMER_SEARCH_NAME: Response status:', response.status);
            
            if (response.data && response.data.QueryResponse && response.data.QueryResponse.Customer) {
                const customers = response.data.QueryResponse.Customer;
                const customerArray = Array.isArray(customers) ? customers : [customers];
                
                console.error('CUSTOMER_SEARCH_NAME: Found customers with name:', customerArray.length);
                
                // Return the first customer found (QuickBooks enforces unique display names)
                return customerArray[0];
            }
            
            console.error('CUSTOMER_SEARCH_NAME: No customers found with name:', displayName);
            return null;
        } catch (error) {
            console.error('CUSTOMER_SEARCH_NAME: Error searching for customer:', error.message);
            return null;
        }
    }

    // Get or create customer with smart display name + email matching
    async getOrCreateCustomer(customerInfo, billingAddress) {
        if (!customerInfo) return null;

        const email = customerInfo.email || (billingAddress && billingAddress.email);
        const firstName = customerInfo.firstName || customerInfo.givenName;
        const lastName = customerInfo.lastName || customerInfo.familyName;
        const displayName = customerInfo.displayName || 
                           (firstName && lastName ? `${firstName} ${lastName}` : 
                            firstName || lastName || email || 'Customer');

        // Step 1: Try to find existing customer by email first (most reliable)
        let customer = null;
        if (email) {
            customer = await this.findCustomerByEmail(email);
            if (customer) {
                console.error('CUSTOMER: Found existing customer by email:', customer.Id, customer.DisplayName, '- REUSING EXISTING');
                return customer;
            }
        }

        // Step 2: Check display name variations to find one with matching email OR find available slot
        console.error('CUSTOMER: No customer found by email, checking display name variations');
        
        // Check base display name first: "Elizabeth Morace"
        customer = await this.findCustomerByName(displayName);
        if (customer) {
            const customerEmail = customer.PrimaryEmailAddr?.Address;
            if (email && customerEmail && customerEmail.toLowerCase() === email.toLowerCase()) {
                console.error('CUSTOMER: Found existing customer by name with SAME email:', customer.Id, customer.DisplayName, '- REUSING EXISTING');
                return customer;
            } else {
                console.error('CUSTOMER: Found customer by name but DIFFERENT email:', customer.DisplayName, 'has', customerEmail, 'vs', email, '- checking numbered variations');
            }
        } else {
            console.error('CUSTOMER: Display name available:', displayName, '- will create new customer');
            // Base name is available, we can create with this name
            return await this.createNewCustomer(displayName, customerInfo, billingAddress, email);
        }

        // Step 3: Check numbered variations: "Elizabeth Morace (1)", "Elizabeth Morace (2)", etc.
        for (let i = 1; i <= 10; i++) {
            const numberedDisplayName = `${displayName} (${i})`;
            customer = await this.findCustomerByName(numberedDisplayName);
            
            if (customer) {
                const customerEmail = customer.PrimaryEmailAddr?.Address;
                if (email && customerEmail && customerEmail.toLowerCase() === email.toLowerCase()) {
                    console.error(`CUSTOMER: Found existing customer ${numberedDisplayName} with SAME email:`, customer.Id, '- REUSING EXISTING');
                    return customer;
                } else {
                    console.error(`CUSTOMER: Found customer ${numberedDisplayName} but DIFFERENT email:`, customerEmail, 'vs', email, '- continuing search');
                }
            } else {
                console.error(`CUSTOMER: Display name available: ${numberedDisplayName} - will create new customer`);
                // This numbered variation is available, create customer with this name
                return await this.createNewCustomer(numberedDisplayName, customerInfo, billingAddress, email);
            }
        }
        
        console.error('CUSTOMER: All display name variations (1-10) are taken with different emails - giving up');
        return null;
    }

    // Helper method to create new customer
    async createNewCustomer(displayName, customerInfo, billingAddress, email) {
        const newCustomerData = {
            displayName: displayName,
            email: email,
            firstName: customerInfo.firstName || customerInfo.givenName,
            lastName: customerInfo.lastName || customerInfo.familyName,
            phone: customerInfo.phone || (billingAddress && billingAddress.phone),
            companyName: customerInfo.companyName || customerInfo.company,
            billingAddress: billingAddress ? {
                line1: billingAddress.line1 || billingAddress.address1 || billingAddress.street,
                line2: billingAddress.line2 || billingAddress.address2,
                city: billingAddress.city,
                state: billingAddress.state || billingAddress.countrySubDivisionCode,
                postalCode: billingAddress.postalCode || billingAddress.zipCode || billingAddress.zip,
                country: billingAddress.country || 'US'
            } : undefined
        };

        try {
            const customer = await this.createCustomerFromData(newCustomerData);
            console.error('CUSTOMER: Successfully created new customer:', customer.Id, 'with display name:', displayName);
            return customer;
        } catch (error) {
            console.error('CUSTOMER: Failed to create customer with name:', displayName, 'Error:', error.message);
            return null;
        }
    }

    // Create Sales Receipt in QuickBooks Online to link payment to customer
    async createSalesReceipt(customer, paymentData, req, paymentType = 'payment') {
        try {
            console.error('SALES_RECEIPT: Creating sales receipt for customer:', customer.Id);
            
            const salesReceiptData = {
                CustomerRef: {
                    value: customer.Id,
                    name: customer.DisplayName
                },
                TxnDate: new Date().toISOString().split('T')[0], // Today's date in YYYY-MM-DD format
                Line: [
                    {
                        Amount: parseFloat(paymentData.amount).toFixed(2),
                        DetailType: "SalesItemLineDetail",
                        SalesItemLineDetail: {
                            ItemRef: {
                                value: "1", // Default service item - you may need to adjust this
                                name: "Services"
                            }
                        }
                    }
                ],
                TotalAmt: parseFloat(paymentData.amount).toFixed(2),
                PrivateNote: `${paymentType.charAt(0).toUpperCase() + paymentType.slice(1)} processed via QuickBooks Payments API. Transaction ID: ${paymentData.id}`,
                DocNumber: paymentData.id, // Use payment transaction ID as document number
                PaymentMethodRef: {
                    value: "1" // Default to Credit Card - you may need to adjust this
                }
            };

            console.error('SALES_RECEIPT: Sending sales receipt data:', JSON.stringify(salesReceiptData, null, 2));
            
            const response = await this.makeQBOApiRequest('salesreceipt', 'POST', salesReceiptData);
            
            console.error('SALES_RECEIPT: Response status:', response.status);
            console.error('SALES_RECEIPT: Response data:', JSON.stringify(response.data, null, 2));
            
            // Check if QuickBooks returned an error
            if (response.data && response.data.Fault && response.data.Fault.Error) {
                const qbError = response.data.Fault.Error[0];
                const errorMessage = qbError.Message || qbError.Detail || 'QuickBooks API error';
                throw new Error(errorMessage);
            }
            
            if (response.data && response.data.SalesReceipt) {
                return response.data.SalesReceipt;
            }
            
            return null;
        } catch (error) {
            console.error('SALES_RECEIPT: Error creating sales receipt:', error.message);
            console.error('SALES_RECEIPT: Full error:', JSON.stringify(error, null, 2));
            throw error;
        }
    }

    // Find Sales Receipt by transaction ID (DocNumber)
    async findSalesReceiptByTransactionId(transactionId) {
        try {
            console.error('SALES_RECEIPT_SEARCH: Searching for sales receipt with transaction ID:', transactionId);
            
            const query = `SELECT * FROM SalesReceipt WHERE DocNumber = '${transactionId}'`;
            const encodedQuery = encodeURIComponent(query);
            
            const response = await this.makeQBOApiRequest(`query?query=${encodedQuery}`);
            
            console.error('SALES_RECEIPT_SEARCH: Response status:', response.status);
            console.error('SALES_RECEIPT_SEARCH: Response data:', JSON.stringify(response.data, null, 2));
            
            if (response.data && response.data.QueryResponse && response.data.QueryResponse.SalesReceipt) {
                const salesReceipts = Array.isArray(response.data.QueryResponse.SalesReceipt) 
                    ? response.data.QueryResponse.SalesReceipt 
                    : [response.data.QueryResponse.SalesReceipt];
                
                console.error('SALES_RECEIPT_SEARCH: Found sales receipts:', salesReceipts.length);
                return salesReceipts[0]; // Return first match
            }
            
            console.error('SALES_RECEIPT_SEARCH: No sales receipt found with transaction ID:', transactionId);
            return null;
        } catch (error) {
            console.error('SALES_RECEIPT_SEARCH: Error searching for sales receipt:', error.message);
            return null;
        }
    }

    // Create Refund Receipt in QuickBooks Online
    async createRefundReceipt(customer, originalSalesReceipt, refundData, req) {
        try {
            console.error('REFUND_RECEIPT: Creating refund receipt for customer:', customer.Id);
            console.error('REFUND_RECEIPT: Original sales receipt ID:', originalSalesReceipt?.Id);
            console.error('REFUND_RECEIPT: Refund data:', JSON.stringify(refundData, null, 2));
            
            const refundReceiptData = {
                CustomerRef: {
                    value: customer.Id,
                    name: customer.DisplayName
                },
                TxnDate: new Date().toISOString().split('T')[0], // Today's date in YYYY-MM-DD format
                Line: [
                    {
                        Amount: parseFloat(refundData.amount || originalSalesReceipt.TotalAmt).toFixed(2),
                        DetailType: "SalesItemLineDetail",
                        SalesItemLineDetail: {
                            ItemRef: {
                                value: "1", // Default service item - should match original
                                name: "Services"
                            }
                        }
                    }
                ],
                TotalAmt: parseFloat(refundData.amount || originalSalesReceipt.TotalAmt).toFixed(2),
                PrivateNote: `Refund for transaction ${refundData.originalTransactionId}. ${refundData.reason || 'Customer request'}. Original Sales Receipt: ${originalSalesReceipt?.Id || 'N/A'}`,
                DocNumber: refundData.refundId, // Use refund transaction ID as document number
                PaymentMethodRef: {
                    value: "1" // Default to Credit Card - should match original
                }
            };

            console.error('REFUND_RECEIPT: Sending refund receipt data:', JSON.stringify(refundReceiptData, null, 2));
            
            const response = await this.makeQBOApiRequest('refundreceipt', 'POST', refundReceiptData);
            
            console.error('REFUND_RECEIPT: Response status:', response.status);
            console.error('REFUND_RECEIPT: Response data:', JSON.stringify(response.data, null, 2));
            
            // Check if QuickBooks returned an error
            if (response.data && response.data.Fault && response.data.Fault.Error) {
                const qbError = response.data.Fault.Error[0];
                const errorMessage = qbError.Message || qbError.Detail || 'QuickBooks API error';
                throw new Error(errorMessage);
            }
            
            if (response.data && response.data.RefundReceipt) {
                return response.data.RefundReceipt;
            }
            
            return null;
        } catch (error) {
            console.error('REFUND_RECEIPT: Error creating refund receipt:', error.message);
            console.error('REFUND_RECEIPT: Full error:', JSON.stringify(error, null, 2));
            throw error;
        }
    }

    // Void a Sales Receipt
    async voidSalesReceipt(salesReceiptId, req) {
        try {
            console.error('VOID_SALES_RECEIPT: Voiding sales receipt:', salesReceiptId);
            
            // First, get the current sales receipt to get the SyncToken
            const query = `SELECT * FROM SalesReceipt WHERE Id = '${salesReceiptId}'`;
            const encodedQuery = encodeURIComponent(query);
            
            const getResponse = await this.makeQBOApiRequest(`query?query=${encodedQuery}`);
            
            if (!getResponse.data?.QueryResponse?.SalesReceipt) {
                throw new Error('Sales receipt not found');
            }
            
            const salesReceipt = Array.isArray(getResponse.data.QueryResponse.SalesReceipt)
                ? getResponse.data.QueryResponse.SalesReceipt[0]
                : getResponse.data.QueryResponse.SalesReceipt;
            
            // Create void data
            const voidData = {
                Id: salesReceipt.Id,
                SyncToken: salesReceipt.SyncToken,
                sparse: true
            };
            
            const response = await this.makeQBOApiRequest(`salesreceipt?operation=void`, 'POST', voidData);
            
            console.error('VOID_SALES_RECEIPT: Response status:', response.status);
            console.error('VOID_SALES_RECEIPT: Response data:', JSON.stringify(response.data, null, 2));
            
            if (response.data && response.data.SalesReceipt) {
                return response.data.SalesReceipt;
            }
            
            return null;
        } catch (error) {
            console.error('VOID_SALES_RECEIPT: Error voiding sales receipt:', error.message);
            throw error;
        }
    }
}

module.exports = QuickBooksOnlineService;
