/**
 * @fileoverview iOS test runner for Capacitor-based mobile app
 * 
 * This script handles the build and testing of the iOS app using Xcode's
 * command-line tools. It ensures the app is properly synced with the latest
 * web build and runs the test suite on a specified iOS simulator.
 * 
 * Process flow:
 * 1. Clean and reset iOS platform (if needed)
 * 2. Check prerequisites (Xcode, CocoaPods, Capacitor setup)
 * 3. Sync Capacitor project with latest web build
 * 4. Build app for iOS simulator
 * 5. Run XCTest suite
 * 
 * Prerequisites:
 * - macOS operating system
 * - Xcode installed with command line tools
 * - iOS simulator available
 * - Capacitor iOS platform added to project
 * - Valid iOS development certificates
 * 
 * Exit codes:
 * - 0: Tests completed successfully
 * - 1: Build or test failure
 * 
 * @example
 * // Run directly
 * node scripts/test-ios.js
 * 
 * // Run via npm script
 * npm run test:ios
 * 
 * @requires child_process
 * @requires path
 * @requires fs
 * 
 * @author TimeSafari Team
 * @license MIT
 */

const { execSync } = require('child_process');
const { join } = require('path');
const { existsSync, mkdirSync, appendFileSync, readFileSync, writeFileSync, readdirSync, statSync, accessSync } = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});
const { constants } = require('fs');

const question = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));

// Make sure to close readline at the end
process.on('SIGINT', () => {
    rl.close();
    process.exit();
});

// Format date as YYYY-MM-DD-HHMMSS
const getLogFileName = () => {
    const now = new Date();
    const date = now.toISOString().split('T')[0];
    const time = now.toTimeString().split(' ')[0].replace(/:/g, '');
    return `build_logs/ios-build-${date}-${time}.log`;
};

// Create logger function
const createLogger = (logFile) => {
    return (message) => {
        const timestamp = new Date().toISOString();
        const logMessage = `[${timestamp}] ${message}\n`;
        console.log(message);
        appendFileSync(logFile, logMessage);
    };
};

/**
 * Clean up and reset iOS platform
 * This function completely removes and recreates the iOS platform to ensure a fresh setup
 * @param {function} log - Logging function
 * @returns {boolean} - Success status
 */
const cleanIosPlatform = async (log) => {
    log('๐Ÿงน Cleaning iOS platform (complete reset)...');
    
    // Check for package.json and capacitor.config.ts/js
    if (!existsSync('package.json')) {
        log('โš ๏ธ package.json not found. Are you in the correct directory?');
        throw new Error('package.json not found. Cannot continue without project configuration.');
    }
    log('โœ… package.json exists');
    
    const capacitorConfigExists = 
        existsSync('capacitor.config.ts') || 
        existsSync('capacitor.config.js') || 
        existsSync('capacitor.config.json');
    
    if (!capacitorConfigExists) {
        log('โš ๏ธ Capacitor config file not found');
        log('Creating minimal capacitor.config.ts...');
        
        try {
            // Get app name from package.json
            const packageJson = JSON.parse(readFileSync('package.json', 'utf8'));
            const appName = packageJson.name || 'App';
            const appId = packageJson.capacitor?.appId || 'io.ionic.starter';
            
            // Create a minimal capacitor config
            const capacitorConfig = `
import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: '${appId}',
  appName: '${appName}',
  webDir: 'dist',
  bundledWebRuntime: false
};

export default config;
            `.trim();
            
            writeFileSync('capacitor.config.ts', capacitorConfig);
            log('โœ… Created capacitor.config.ts');
        } catch (configError) {
            log('โš ๏ธ Failed to create Capacitor config file');
            log('Please create a capacitor.config.ts file manually');
            throw new Error('Capacitor configuration missing. Please configure manually.');
        }
    } else {
        log('โœ… Capacitor config exists');
    }
    
    // Check if the platform exists first
    if (existsSync('ios')) {
        log('๐Ÿ—‘๏ธ Removing existing iOS platform directory...');
        try {
            execSync('rm -rf ios', { stdio: 'inherit' });
            log('โœ… Existing iOS platform removed');
        } catch (error) {
            log(`โš ๏ธ Error removing iOS platform: ${error.message}`);
            log('โš ๏ธ You may need to manually remove the ios directory');
            return false;
        }
    }
    
    // Rebuild web assets first to ensure they're available
    log('๐Ÿ”„ Building web assets before adding iOS platform...');
    try {
        execSync('rm -rf dist', { stdio: 'inherit' });
        execSync('npm run build:web', { stdio: 'inherit' });
        execSync('npm run build:capacitor', { stdio: 'inherit' });
        log('โœ… Web assets built successfully');
    } catch (error) {
        log(`โš ๏ธ Error building web assets: ${error.message}`);
        log('โš ๏ธ Continuing with platform addition, but it may fail if web assets are required');
    }
    
    // Add the platform back
    log('โž• Adding iOS platform...');
    try {
        execSync('npx cap add ios', { stdio: 'inherit' });
        log('โœ… iOS platform added successfully');
        
        // Verify critical files were created
        if (!existsSync('ios/App/Podfile')) {
            log('โš ๏ธ Podfile was not created - something is wrong with the Capacitor setup');
            return false;
        }
        
        if (!existsSync('ios/App/App/Info.plist')) {
            log('โš ๏ธ Info.plist was not created - something is wrong with the Capacitor setup');
            return false;
        }
        
        log('โœ… iOS platform setup verified - critical files exist');
        return true;
    } catch (error) {
        log(`โš ๏ธ Error adding iOS platform: ${error.message}`);
        return false;
    }
};

/**
 * Check all prerequisites for iOS testing
 * Verifies and attempts to install/initialize all required components
 */
const checkPrerequisites = async (log) => {
    log('๐Ÿ” Checking prerequisites for iOS testing...');
    
    // Check for macOS
    if (process.platform !== 'darwin') {
        throw new Error('iOS testing is only supported on macOS');
    }
    log('โœ… Running on macOS');
    
    // Verify Xcode installation
    try {
        const xcodeOutput = execSync('xcode-select -p').toString().trim();
        log(`โœ… Xcode command line tools found at: ${xcodeOutput}`);
    } catch (error) {
        log('โš ๏ธ Xcode command line tools not found');
        log('Please install Xcode from the App Store and run:');
        log('xcode-select --install');
        throw new Error('Xcode command line tools not found. Please install Xcode first.');
    }
    
    // Check Xcode version
    try {
        const xcodeVersionOutput = execSync('xcodebuild -version').toString().trim();
        log(`โœ… Xcode version: ${xcodeVersionOutput.split('\n')[0]}`);
    } catch (error) {
        log('โš ๏ธ Unable to determine Xcode version');
    }
    
    // Check for CocoaPods
    try {
        const podVersionOutput = execSync('pod --version').toString().trim();
        log(`โœ… CocoaPods version: ${podVersionOutput}`);
    } catch (error) {
        log('โš ๏ธ CocoaPods not found');
        log('Attempting to install CocoaPods...');
        
        try {
            log('๐Ÿ”„ Installing CocoaPods via gem...');
            execSync('gem install cocoapods', { stdio: 'inherit' });
            log('โœ… CocoaPods installed successfully');
        } catch (gemError) {
            log('โš ๏ธ Failed to install CocoaPods via gem');
            log('Please install CocoaPods manually:');
            log('1. sudo gem install cocoapods');
            log('2. brew install cocoapods');
            throw new Error('CocoaPods installation failed. Please install manually.');
        }
    }
    
    log('โœ… All prerequisites for iOS testing are met');
    return true;
};

// Check for iOS simulator
const checkSimulator = async (log) => {
    log('๐Ÿ” Checking for iOS simulator...');
    const simulatorsOutput = execSync('xcrun simctl list devices available -j').toString();
    const simulatorsData = JSON.parse(simulatorsOutput);
    
    // Get all available devices/simulators with their UDIDs
    const allDevices = [];
    
    // Process all runtime groups (iOS versions)
    Object.entries(simulatorsData.devices).forEach(([runtime, devices]) => {
        devices.forEach(device => {
            allDevices.push({
                name: device.name,
                udid: device.udid,
                state: device.state,
                runtime: runtime,
                isIphone: device.name.includes('iPhone'),
            });
        });
    });
    
    // Check for booted simulators first
    const bootedDevices = allDevices.filter(device => device.state === 'Booted');
    
    if (bootedDevices.length > 0) {
        log(`๐Ÿ“ฑ Found ${bootedDevices.length} running simulator(s): ${bootedDevices.map(d => d.name).join(', ')}`);
        return bootedDevices;
    }
    
    // No booted devices found, try to boot one
    log('โš ๏ธ No running iOS simulator found. Attempting to boot one...');
    
    // Prefer iPhone devices, especially newer models
    const preferredDevices = [
        'iPhone 15', 'iPhone 14', 'iPhone 13', 'iPhone 12', 'iPhone',  // Prefer newer iPhones first
        'iPad'  // Then iPads if no iPhones available
    ];
    
    let deviceToLaunch = null;
    
    // Try to find a device from our preferred list
    for (const preferredName of preferredDevices) {
        const matchingDevices = allDevices.filter(device => 
            device.name.includes(preferredName) && device.state === 'Shutdown');
        
        if (matchingDevices.length > 0) {
            // Sort by runtime to prefer newer iOS versions
            matchingDevices.sort((a, b) => b.runtime.localeCompare(a.runtime));
            deviceToLaunch = matchingDevices[0];
            break;
        }
    }
    
    // If no preferred device found, take any available device
    if (!deviceToLaunch && allDevices.length > 0) {
        const availableDevices = allDevices.filter(device => device.state === 'Shutdown');
        if (availableDevices.length > 0) {
            deviceToLaunch = availableDevices[0];
        }
    }
    
    if (!deviceToLaunch) {
        throw new Error('No available iOS simulators found. Please create a simulator in Xcode first.');
    }
    
    // Boot the selected simulator
    log(`๐Ÿš€ Booting iOS simulator: ${deviceToLaunch.name} (${deviceToLaunch.runtime})`);
    execSync(`xcrun simctl boot ${deviceToLaunch.udid}`);
    
    // Wait for simulator to fully boot
    log('โณ Waiting for simulator to boot completely...');
    // Give the simulator time to fully boot before proceeding
    await new Promise(resolve => setTimeout(resolve, 10000));
    
    log(`โœ… Successfully booted simulator: ${deviceToLaunch.name}`);
    
    return [{ name: deviceToLaunch.name, udid: deviceToLaunch.udid }];
};

// Verify Xcode installation
const verifyXcodeInstallation = (log) => {
    log('๐Ÿ” Checking Xcode installation...');
    try {
        execSync('xcode-select -p');
        log('โœ… Xcode command line tools found');
    } catch (error) {
        throw new Error('Xcode command line tools not found. Please install Xcode first.');
    }
};

// Generate test data using generate_data.ts
const generateTestData = async (log) => {
    log('\n๐Ÿ” DEBUG: Starting test data generation...');
    
    // Check directory structure
    log('๐Ÿ“ Current directory:', process.cwd());
    log('๐Ÿ“ Directory contents:', require('fs').readdirSync('.'));
    
    if (!existsSync('.generated')) {
        log('๐Ÿ“ Creating .generated directory');
        mkdirSync('.generated', { recursive: true });
    }

    try {
        log('๐Ÿ”„ Attempting to run generate_data.ts...');
        execSync('npx ts-node test-scripts/generate_data.ts', { stdio: 'inherit' });
        log('โœ… Test data generation completed');
        
        // Verify and log generated files content
        const requiredFiles = [
            '.generated/test-env.json',
            '.generated/claim_details.json',
            '.generated/contacts.json'
        ];
        
        log('\n๐Ÿ“ Verifying generated files:');
        for (const file of requiredFiles) {
            if (!existsSync(file)) {
                log(`โŒ Missing file: ${file}`);
            } else {
                const content = readFileSync(file, 'utf8');
                log(`\n๐Ÿ“„ Content of ${file}:`);
                log(content);
                try {
                    const parsed = JSON.parse(content);
                    if (file.includes('test-env.json')) {
                        log('๐Ÿ”‘ CONTACT1_DID in test-env:', parsed.CONTACT1_DID);
                    }
                    if (file.includes('contacts.json')) {
                        log('๐Ÿ‘ฅ First contact DID:', parsed[0]?.did);
                    }
                } catch (e) {
                    log(`โŒ Error parsing ${file}:`, e);
                }
            }
        }
    } catch (error) {
        log(`\nโš ๏ธ Test data generation failed: ${error.message}`);
        log('โš ๏ธ Creating fallback test data...');
        
        // Create fallback data with detailed logging
        const fallbackTestEnv = {
            "CONTACT1_DID": "did:ethr:0x35A71Ac3fA0A4D5a4903f10F0f7A3ac4034FaB5B",
            "APP_URL": "https://app.timesafari.example"
        };
        
        const fallbackContacts = [
            {
                "id": "contact1",
                "name": "Test Contact",
                "did": "did:ethr:0x35A71Ac3fA0A4D5a4903f10F0f7A3ac4034FaB5B"
            }
        ];
        
        log('\n๐Ÿ“ Writing fallback data:');
        log('TestEnv:', JSON.stringify(fallbackTestEnv, null, 2));
        log('Contacts:', JSON.stringify(fallbackContacts, null, 2));
        
        writeFileSync('.generated/test-env.json', JSON.stringify(fallbackTestEnv, null, 2));
        writeFileSync('.generated/contacts.json', JSON.stringify(fallbackContacts, null, 2));
        
        // Verify fallback data was written
        log('\n๐Ÿ” Verifying fallback data:');
        try {
            const writtenTestEnv = JSON.parse(readFileSync('.generated/test-env.json', 'utf8'));
            const writtenContacts = JSON.parse(readFileSync('.generated/contacts.json', 'utf8'));
            log('Written TestEnv:', writtenTestEnv);
            log('Written Contacts:', writtenContacts);
        } catch (e) {
            log('โŒ Error verifying fallback data:', e);
        }
    }
};

// Build web assets
const buildWebAssets = async (log) => {
    log('๐ŸŒ Building web assets...');
    execSync('rm -rf dist', { stdio: 'inherit' });
    execSync('npm run build:web', { stdio: 'inherit' });
    execSync('npm run build:capacitor', { stdio: 'inherit' });
    log('โœ… Web assets built successfully');
};

// Configure iOS project
const configureIosProject = async (log) => {
    log('๐Ÿ“ฑ Configuring iOS project...');
    
    // Skip cap sync since we just did a clean platform add
    log('โœ… Using freshly created iOS platform');

    // Register URL scheme for deeplink tests
    log('๐Ÿ”— Configuring URL scheme for deeplink tests...');
    if (checkAndRegisterUrlScheme(log)) {
        log('โœ… URL scheme configuration completed');
    } else {
        log('โš ๏ธ URL scheme could not be registered automatically');
        log('โš ๏ธ Deeplink tests may not work correctly');
    }

    log('โš™๏ธ Installing CocoaPods dependencies...');
    try {
        // Try to run pod install normally first
        log('๐Ÿ”„ Running "pod install" in ios/App directory...');
        execSync('cd ios/App && pod install', { stdio: 'inherit' });
        log('โœ… CocoaPods installation completed');
    } catch (error) {
        // If that fails, provide detailed instructions
        log(`โš ๏ธ CocoaPods installation failed: ${error.message}`);
        log('โš ๏ธ Please ensure CocoaPods is installed correctly:');
        log('1. If using system Ruby: "sudo gem install cocoapods"');
        log('2. If using Homebrew Ruby: "brew install cocoapods"');
        log('3. Then run: "cd ios/App && pod install"');
        
        // Try to continue despite the error
        log('โš ๏ธ Attempting to continue with the build process...');
    }
    
    // Add information about iOS security dialogs
    log('\n๐Ÿ“ฑ iOS Security Dialog Information:');
    log('โš ๏ธ iOS will display security confirmation dialogs when testing deeplinks');
    log('โš ๏ธ This is a security feature of iOS and cannot be bypassed in normal testing');
    log('โš ๏ธ You will need to manually approve each deeplink test by clicking "Open" in the dialog');
    log('โš ๏ธ The app must be running in the foreground for deeplinks to work properly');
    log('โš ๏ธ If tests appear to hang, check if a security dialog is waiting for your confirmation');
};

// Build and test iOS project
const buildAndTestIos = async (log, simulator) => {
    const simulatorName = simulator[0].name;
    log('๐Ÿ—๏ธ Building iOS project...');
    execSync('cd ios/App && xcodebuild clean -workspace App.xcworkspace -scheme App', { stdio: 'inherit' });
    log('โœ… Xcode clean completed');
    
    log(`๐Ÿ—๏ธ Building for simulator: ${simulatorName}`);
    execSync(`cd ios/App && xcodebuild build -workspace App.xcworkspace -scheme App -destination "platform=iOS Simulator,name=${simulatorName}"`, { stdio: 'inherit' });
    log('โœ… Xcode build completed');

    // Check if the project is configured for testing by querying the scheme capabilities
    try {
        log(`๐Ÿงช Checking if scheme is configured for testing`);
        const schemeInfo = execSync(`cd ios/App && xcodebuild -scheme App -showBuildSettings | grep TEST`).toString();
        
        if (schemeInfo.includes('ENABLE_TESTABILITY = YES')) {
            log(`๐Ÿงช Attempting to run tests on simulator: ${simulatorName}`);
            try {
                execSync(`cd ios/App && xcodebuild test -workspace App.xcworkspace -scheme App -destination "platform=iOS Simulator,name=${simulatorName}"`, { stdio: 'inherit' });
                log('โœ… iOS tests completed successfully');
            } catch (testError) {
                log(`โš ๏ธ Tests failed or scheme not properly configured for testing: ${testError.message}`);
                log('โš ๏ธ This is normal if no test targets have been added to the project');
                log('โš ๏ธ Skipping test step and continuing with the app launch');
            }
        } else {
            log('โš ๏ธ Project does not have testing enabled in build settings');
            log('โš ๏ธ Skipping test step and continuing with the app launch');
        }
    } catch (error) {
        log('โš ๏ธ Unable to determine if testing is configured');
        log('โš ๏ธ Skipping test step and continuing with the app launch');
    }
};

// Run the app
const runIosApp = async (log, simulator) => {
    const simulatorName = simulator[0].name;
    const simulatorUdid = simulator[0].udid;
    
    log(`๐Ÿ“ฑ Running app in simulator: ${simulatorName} (${simulatorUdid})...`);
    // Use the --target parameter to specify the device directly, avoiding the UI prompt
    execSync(`npx cap run ios --target="${simulatorUdid}"`, { stdio: 'inherit' });
    log('โœ… App launched successfully');
};

const validateTestData = (log) => {
    log('\n=== VALIDATING TEST DATA ===');
    
    const generateFreshTestData = () => {
        log('\n๐Ÿ”„ Generating fresh test data...');
        try {
            // Ensure .generated directory exists
            if (!existsSync('.generated')) {
                mkdirSync('.generated', { recursive: true });
            }

            // Execute the generate_data.ts script synchronously
            log('Running generate_data.ts...');
            execSync('npx ts-node test-scripts/generate_data.ts', { 
                stdio: 'inherit',
                encoding: 'utf8'
            });

            // Read and validate the generated files
            const testEnvPath = '.generated/test-env.json';
            const contactsPath = '.generated/contacts.json';
            
            if (!existsSync(testEnvPath) || !existsSync(contactsPath)) {
                throw new Error('Generated files not found after running generate_data.ts');
            }

            const testEnv = JSON.parse(readFileSync(testEnvPath, 'utf8'));
            const contacts = JSON.parse(readFileSync(contactsPath, 'utf8'));

            // Validate required fields
            if (!testEnv.CONTACT1_DID) {
                throw new Error('CONTACT1_DID missing from generated test data');
            }

            log('Generated test data:', {
                testEnv: testEnv,
                contacts: contacts
            });

            return { testEnv, contacts };
        } catch (error) {
            log('โŒ Test data generation failed:', error);
            throw error;
        }
    };

    try {
        // Try to read existing data or generate fresh data
        const testEnvPath = '.generated/test-env.json';
        const contactsPath = '.generated/contacts.json';
        
        let testData;
        
        // If either file is missing or invalid, generate fresh data
        if (!existsSync(testEnvPath) || !existsSync(contactsPath)) {
            testData = generateFreshTestData();
        } else {
            try {
                const testEnv = JSON.parse(readFileSync(testEnvPath, 'utf8'));
                const contacts = JSON.parse(readFileSync(contactsPath, 'utf8'));
                
                // Validate required fields
                if (!testEnv.CLAIM_ID || !testEnv.CONTACT1_DID) {
                    log('โš ๏ธ Existing test data missing required fields, regenerating...');
                    testData = generateFreshTestData();
                } else {
                    testData = { testEnv, contacts };
                }
            } catch (error) {
                log('โš ๏ธ Error reading existing test data, regenerating...');
                testData = generateFreshTestData();
            }
        }
        
        // Final validation of data
        if (!testData.testEnv.CLAIM_ID || !testData.testEnv.CONTACT1_DID) {
            throw new Error('Test data validation failed even after generation');
        }
        
        log('โœ… Test data validated successfully');
        log('๐Ÿ“„ Test Environment:', JSON.stringify(testData.testEnv, null, 2));
        
        return testData;
        
    } catch (error) {
        log(`โŒ Test data validation failed: ${error.message}`);
        throw error;
    }
};

/**
 * Run deeplink tests 
 * Optionally tests deeplinks if the test data is available
 * 
 * @param {function} log - Logging function
 * @returns {Promise<void>}
 */
const runDeeplinkTests = async (log) => {
    log('\n=== Starting Deeplink Tests ===');
    
    // Validate test data before proceeding
    let testEnv, contacts;
    try {
        ({ testEnv, contacts } = validateTestData(log));
    } catch (error) {
        log('โŒ Cannot proceed with tests due to invalid test data');
        log(`Error: ${error.message}`);
        log('Please ensure test data is properly generated before running tests');
        process.exit(1); // Exit with error code
    }
    
    // Now we can safely create the deeplink tests knowing we have valid data
    const deeplinkTests = [
        {
            url: `timesafari://claim/${testEnv.CLAIM_ID}`,
            description: 'Claim view'
        },
        {
            url: `timesafari://claim-cert/${testEnv.CERT_ID || testEnv.CLAIM_ID}`,
            description: 'Claim certificate view'
        },
        {
            url: `timesafari://claim-add-raw/${testEnv.RAW_CLAIM_ID || testEnv.CLAIM_ID}`,
            description: 'Raw claim addition'
        },
        {
            url: 'timesafari://did/test',
            description: 'DID view with test identifier'
        },
        {
            url: `timesafari://did/${testEnv.CONTACT1_DID}`,
            description: 'DID view with contact DID'
        },
        {
            url: (() => {
                if (!testEnv?.CONTACT1_DID) {
                    throw new Error('Cannot construct contact-edit URL: CONTACT1_DID is missing');
                }
                const url = `timesafari://contact-edit/${testEnv.CONTACT1_DID}`;
                log('Created contact-edit URL:', url);
                return url;
            })(),
            description: 'Contact editing'
        },
        {
            url: `timesafari://contacts/import?contacts=${encodeURIComponent(JSON.stringify(contacts))}`,
            description: 'Contacts import'
        }
    ];

    // Log the final test configuration
    log('\n5. Final Test Configuration:');
    deeplinkTests.forEach((test, i) => {
        log(`\nTest ${i + 1}:`);
        log(`Description: ${test.description}`);
        log(`URL: ${test.url}`);
    });
    
    // Show instructions for iOS security dialogs
    log('\n๐Ÿ“ฑ IMPORTANT: iOS Security Dialog Instructions:');
    log('1. Each deeplink test will trigger a security confirmation dialog');
    log('2. You MUST click "Open" on each dialog to continue testing');
    log('3. The app must be running in the FOREGROUND');
    log('4. You will need to press Enter in this terminal after handling each dialog');
    log('5. You can abort the testing process by pressing Ctrl+C\n');
    
    // Ensure app is in foreground
    log('โš ๏ธ IMPORTANT: Please make sure the app is in the FOREGROUND now');
    await question('Press Enter when the app is visible and in the foreground...');
    
    try {
        // Execute each test
        let testsCompleted = 0;
        let testsSkipped = 0;
        
        for (const test of deeplinkTests) {
            // Show upcoming test info before execution
            log('\n๐Ÿ“ฑ NEXT TEST:');
            log('------------------------');
            log(`Description: ${test.description}`);
            log(`URL to test: ${test.url}`);
            log('------------------------');
            
            // Clear prompt for user action
            await question('\nโŽ  Press Enter to execute this test (or Ctrl+C to quit)...');
            
            try {
                log('๐Ÿš€ Executing deeplink test...');
                log('โš ๏ธ iOS SECURITY DIALOG WILL APPEAR - Click "Open" to continue');
                
                execSync(`xcrun simctl openurl booted "${test.url}"`, { stdio: 'pipe' });
                log(`โœ… Successfully executed: ${test.description}`);
                testsCompleted++;
                
                // Show progress
                log(`\n๐Ÿ“Š Progress: ${testsCompleted}/${deeplinkTests.length} tests completed`);
                
                // If there are more tests, show the next one
                if (testsCompleted < deeplinkTests.length) {
                    const nextTest = deeplinkTests[testsCompleted];
                    log('\nโญ๏ธ  NEXT UP:');
                    log('------------------------');
                    log(`Next test will be: ${nextTest.description}`);
                    log(`URL: ${nextTest.url}`);
                    log('------------------------');
                    await question('\nโŽ  Press Enter when ready for the next test...');
                }
            } catch (deeplinkError) {
                const errorMessage = deeplinkError.message || '';
                
                // Handle specific error for URL scheme not registered
                if (errorMessage.includes('OSStatus error -10814') || errorMessage.includes('NSOSStatusErrorDomain, code=-10814')) {
                    log(`โš ๏ธ URL scheme not properly handled: ${test.description}`);
                    testsSkipped++;
                } else {
                    log(`โš ๏ธ Failed to execute deeplink test: ${test.description}`);
                    log(`โš ๏ธ Error: ${errorMessage}`);
                }
                log('โš ๏ธ Continuing with next test...');
                
                // Show next test info after error handling
                if (testsCompleted + testsSkipped < deeplinkTests.length) {
                    const nextTest = deeplinkTests[testsCompleted + testsSkipped];
                    log('\nโญ๏ธ  NEXT UP:');
                    log('------------------------');
                    log(`Next test will be: ${nextTest.description}`);
                    log(`URL: ${nextTest.url}`);
                    log('------------------------');
                    await question('\nโŽ  Press Enter when ready for the next test...');
                }
            }
        }

        log('\n๐ŸŽ‰ All deeplink tests completed!');
        log(`โœ… Successful: ${testsCompleted}`);
        log(`โš ๏ธ Skipped: ${testsSkipped}`);
        
        if (testsSkipped > 0) {
            log('\n๐Ÿ“ Note about skipped tests:');
            log('1. The app needs to have the URL scheme registered in Info.plist');
            log('2. The app needs to be rebuilt after registering the URL scheme');
            log('3. The app must be running in the foreground for deeplink tests to work');
            log('4. iOS security dialogs must be manually approved for each deeplink test');
            log('5. If these conditions are met and tests still fail, check URL handling in the app code');
        }
    } catch (error) {
        log(`โŒ Deeplink tests setup failed: ${error.message}`);
        log('โš ๏ธ Deeplink tests might be unavailable or test data is missing');
    }
};

// Check and register URL scheme if needed
const checkAndRegisterUrlScheme = (log) => {
    log('๐Ÿ” Checking if URL scheme is registered in Info.plist...');
    
    const infoPlistPath = 'ios/App/App/Info.plist';
    
    // Check if Info.plist exists
    if (!existsSync(infoPlistPath)) {
        log('โš ๏ธ Info.plist not found at: ' + infoPlistPath);
        return false;
    }
    
    // Read Info.plist content
    const infoPlistContent = readFileSync(infoPlistPath, 'utf8');
    
    // Check if URL scheme is already registered
    if (infoPlistContent.includes('<string>timesafari</string>')) {
        log('โœ… URL scheme "timesafari://" is already registered in Info.plist');
        return true;
    }
    
    log('โš ๏ธ URL scheme "timesafari://" is not registered in Info.plist');
    log('โš ๏ธ Attempting to register the URL scheme automatically...');
    
    try {
        // Look for the closing dict tag to insert our URL types
        const closingDictIndex = infoPlistContent.lastIndexOf('</dict>');
        if (closingDictIndex === -1) {
            log('โš ๏ธ Could not find closing dict tag in Info.plist');
            return false;
        }
        
        // Create URL types entry
        const urlTypesEntry = `
	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleURLName</key>
			<string>app.timesafari</string>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>timesafari</string>
			</array>
		</dict>
	</array>`;
        
        // Insert URL types entry before closing dict
        const updatedPlistContent = 
            infoPlistContent.substring(0, closingDictIndex) + 
            urlTypesEntry + 
            infoPlistContent.substring(closingDictIndex);
        
        // Write updated content back to Info.plist
        const { writeFileSync } = require('fs');
        writeFileSync(infoPlistPath, updatedPlistContent, 'utf8');
        
        log('โœ… URL scheme "timesafari://" registered in Info.plist');
        log('โš ๏ธ You will need to rebuild the app for changes to take effect');
        return true;
    } catch (error) {
        log(`โš ๏ธ Failed to register URL scheme: ${error.message}`);
        return false;
    }
};

// Helper function to get the app identifier from package.json or capacitor config
const getAppIdentifier = () => {
    try {
        // Try to read from capacitor.config.ts/js/json
        if (existsSync('capacitor.config.json')) {
            const config = JSON.parse(readFileSync('capacitor.config.json', 'utf8'));
            return config.appId;
        } 
        if (existsSync('capacitor.config.js')) {
            // We can't directly require the file, but we can try to extract the appId
            const content = readFileSync('capacitor.config.js', 'utf8');
            const match = content.match(/appId:\s*['"]([^'"]+)['"]/);
            if (match && match[1]) return match[1];
        }
        if (existsSync('capacitor.config.ts')) {
            // Similar approach for TypeScript
            const content = readFileSync('capacitor.config.ts', 'utf8');
            const match = content.match(/appId:\s*['"]([^'"]+)['"]/);
            if (match && match[1]) return match[1];
        }
        
        // Fall back to package.json
        const packageJson = JSON.parse(readFileSync('package.json', 'utf8'));
        if (packageJson.capacitor && packageJson.capacitor.appId) {
            return packageJson.capacitor.appId;
        }
        
        // Default fallback
        return 'app.timesafari';
    } catch (error) {
        console.error('Error getting app identifier:', error);
        return 'app.timesafari'; // Default fallback
    }
};

/**
 * Runs the complete iOS test suite including build and testing
 * 
 * The function performs the following steps:
 * 1. Cleans and resets the iOS platform
 * 2. Verifies prerequisites and project setup
 * 3. Syncs the Capacitor project with latest web build
 * 4. Builds the app using xcodebuild
 * 5. Optionally runs tests if configured
 * 6. Launches the app in the simulator
 * 
 * If no simulator is running, it automatically selects and boots one.
 * 
 * @async
 * @throws {Error} If any step in the build process fails
 * 
 * @example
 * runIosTests().catch(error => {
 *     console.error('Test execution failed:', error);
 *     process.exit(1);
 * });
 */
async function runIosTests() {
    // Create build_logs directory if it doesn't exist
    if (!existsSync('build_logs')) {
        mkdirSync('build_logs');
    }

    const logFile = getLogFileName();
    const log = createLogger(logFile);

    try {
        log('๐Ÿš€ Starting iOS build and test process...');

        // Clean and reset iOS platform first
        const cleanSuccess = await cleanIosPlatform(log);
        if (!cleanSuccess) {
            throw new Error('Failed to clean and reset iOS platform. Please check the logs for details.');
        }

        // Check prerequisites
        await checkPrerequisites(log);

        // Generate test data
        await generateTestData(log);

        // Verify Xcode installation
        verifyXcodeInstallation(log);
        
        // Check for simulator or boot one if needed
        const simulator = await checkSimulator(log);
        
        // Configure iOS project
        await configureIosProject(log);
        
        // Build and test using the selected simulator
        await buildAndTestIos(log, simulator);
        
        // Run the app in the simulator
        await runIosApp(log, simulator);

        // Run deeplink tests after app is installed
        await runDeeplinkTests(log);

        log('๐ŸŽ‰ iOS build and test process completed successfully');
        log(`๐Ÿ“ Full build log available at: ${logFile}`);
    } catch (error) {
        log(`โŒ iOS tests failed: ${error.message}`);
        log(`๐Ÿ“ Check build log for details: ${logFile}`);
        process.exit(1);
    }
}

// Execute the test suite
runIosTests();