import {
    ConfigPlugin, withInfoPlist,
    withAppDelegate,
    withEntitlementsPlist,
    withDangerousMod
} from "@expo/config-plugins";
import { promises as fs } from 'fs';
import path from 'path';
import fSystem from 'fs';
import { ExpoConfig } from "@expo/config-types";
import { SmartechBaseProps } from "./smartechBaseProps";
import { appdelegateSnippet, smartechBaseSnippet, swiftImports, smartechBaseSwiftSnippet, deepLinkHandlerSwiftSnippet } from "./iosConstants";
import { Helper } from "./helper";

type URLSchemeEntry = {
    CFBundleTypeRole: string;
    CFBundleURLName: string;
    CFBundleURLSchemes: string[];
};

const addURLSchemeEntry = (
    existingURLTypes: URLSchemeEntry[],
    urlName: string,
    urlSchema: string
): URLSchemeEntry[] => {
    const newEntry: URLSchemeEntry = {
        CFBundleTypeRole: "Editor",
        CFBundleURLName: urlName,
        CFBundleURLSchemes: [urlSchema],
    };

    const isNewEntryAlreadyAdded = existingURLTypes.some((entry) => (
        entry.CFBundleURLName === newEntry.CFBundleURLName &&
        entry.CFBundleURLSchemes.includes(newEntry.CFBundleURLSchemes[0])
    ));

    if (!isNewEntryAlreadyAdded) {
        existingURLTypes.push(newEntry);
    }

    return existingURLTypes;
};

// Set custom entitlements plist
const modifyEntitlementsPlist: ConfigPlugin<SmartechBaseProps> = (config, props) => {
    return withEntitlementsPlist(config, async (newConfig) => {
        newConfig.modResults = {
            ...newConfig.modResults,
            "com.apple.security.application-groups": [`group.${props.ios.groupIdentifier}`],
        };
        return newConfig;
    });
};


// Set custom info plist
const modifyInfoPlist: ConfigPlugin<SmartechBaseProps> = (config, props) => {
    return withInfoPlist(config, (newConfig) => {
        newConfig.modResults = {
            ...newConfig.modResults,
            SmartechKeys: {
                SmartechAppGroup: `group.${props.ios.groupIdentifier}`,
                SmartechAppId: props.ios.appId,
                SmartechAutoFetchLocation: props.ios.autoFetchLocation,
                SmartechUseAdvId: props.ios.useAdvID,
            },
            ...(props.ios.smartechNudges.smartechNudgesEnabled && {
                HanselKeys: {
                    HanselAppId: props.ios.smartechNudges.appId,
                    HanselAppKey: props.ios.smartechNudges.appKey,
                }
            })
        };
        return newConfig;
    });
};

const addSmartechURLScheme: ConfigPlugin<SmartechBaseProps> = (config, props) => {
    return withInfoPlist(config, (newConfig) => {
        if (!props.ios.addTestDevice) {
            return newConfig;
        }

        if (props.ios.testDeviceURLName == undefined || props.ios.testDeviceURLSchema == undefined) {
            console.log(`Smartech Url scheme ${props.ios.testDeviceURLName} and ${props.ios.testDeviceURLSchema}`);
            return newConfig;
        }

        const modResults = newConfig.modResults;
        const existingURLTypes = (modResults.CFBundleURLTypes || []) as URLSchemeEntry[];
        modResults.CFBundleURLTypes = addURLSchemeEntry(
            existingURLTypes,
            props.ios.testDeviceURLName,
            props.ios.testDeviceURLSchema
        );
        return newConfig;
    });
};

const addHanselURLScheme: ConfigPlugin<SmartechBaseProps> = (config, props) => {
    return withInfoPlist(config, (newConfig) => {
        if (!props.ios.smartechNudges.smartechNudgesEnabled || !props.ios.smartechNudges.addTestDevice) {
            return newConfig;
        }

        if (props.ios.smartechNudges?.testDeviceURLName == undefined || props.ios.smartechNudges?.testDeviceURLSchema == undefined) {
            console.log(`Smartech Nudge Url scheme ${props.ios.smartechNudges?.testDeviceURLName} and ${props.ios.smartechNudges?.testDeviceURLSchema}`);
            return newConfig;
        }

        const modResults = newConfig.modResults;
        const existingURLTypes = (modResults.CFBundleURLTypes || []) as URLSchemeEntry[];
        modResults.CFBundleURLTypes = addURLSchemeEntry(
            existingURLTypes,
            props.ios.smartechNudges.testDeviceURLName,
            props.ios.smartechNudges.testDeviceURLSchema
        );
        return newConfig;
    });
};

const addSmartechHeaderMod: ConfigPlugin<SmartechBaseProps> = (config, props) => {
    return withDangerousMod(config, [
        'ios',
        async (config) => {
            const { platformProjectRoot, projectName } = config.modRequest;

            if (projectName && platformProjectRoot) {
                const isSwift = await Helper.Ios.isSwiftProject(platformProjectRoot, projectName);

                if (isSwift) {
                    // For Swift projects, add imports to AppDelegate.swift
                    try {
                        const appDelegateSwiftPath = path.join(platformProjectRoot, projectName, 'AppDelegate.swift');
                        const appDelegateSwiftContents = await fs.readFile(appDelegateSwiftPath, 'utf-8');
                        //console.log(`appdelegate content before  is ${appDelegateSwiftContents}`);
                        console.log(`appdelegate path is ${appDelegateSwiftPath}`);
                        // Add Smartech import if not already present (check for exact import line)
                        const lines = appDelegateSwiftContents.split('\n');
                        const hasSmartechImport = lines.some(line => line.trim() === swiftImports.smartechImport);
                        if (!hasSmartechImport) {
                            let insertIndex = 0;

                            // Find the last import statement
                            for (let i = 0; i < lines.length; i++) {
                                if (lines[i].trim().startsWith('import ')) {
                                    insertIndex = i + 1;
                                } else if (lines[i].trim() !== '' && !lines[i].trim().startsWith('import ')) {
                                    break;
                                }
                            }

                            // Insert the Smartech import
                            lines.splice(insertIndex, 0, swiftImports.smartechImport);
                            const modifiedContents = lines.join('\n');
                            //console.log(`appdelegate content is ${modifiedContents}`);
                            await fs.writeFile(appDelegateSwiftPath, modifiedContents, 'utf-8');
                            console.log(`${swiftImports.smartechImport} added to AppDelegate.swift at line ${insertIndex + 1}`);
                        } else {
                            console.log(`${swiftImports.smartechImport} is already added to AppDelegate.swift`);
                        }
                    } catch (error) {
                        console.error('Error modifying AppDelegate.swift:', error);
                    }
                } else {
                    // For Objective-C projects (existing logic)
                    try {
                        const appDelegateHeaderPath = path.join(platformProjectRoot, projectName, appdelegateSnippet.fileName);
                        const appDelegateHeaderContents = await fs.readFile(appDelegateHeaderPath, 'utf-8');
                        // Add Smartech header snipper if not already present.
                        if (!appDelegateHeaderContents.includes(appdelegateSnippet.smartechHeaderSnippet)) {
                            const modifiedAppDelegateHeaderContents = `${appDelegateHeaderContents}\n${appdelegateSnippet.smartechHeaderSnippet}`;
                            await fs.writeFile(appDelegateHeaderPath, modifiedAppDelegateHeaderContents, 'utf-8');
                        } else {
                            console.log(`${appdelegateSnippet.smartechHeaderSnippet} is already added.`)
                        }

                    } catch (error) {
                        console.error('Error modifying AppDelegate.h:', error);
                    }
                }
            }
            return config;
        }
    ]);
};

const addSmartechHanseleHeaderMod: ConfigPlugin<SmartechBaseProps> = (config, props) => {
    return withDangerousMod(config, [
        'ios',
        async (config) => {
            const { platformProjectRoot, projectName } = config.modRequest;

            if (platformProjectRoot && projectName) {
                const isSwift = await Helper.Ios.isSwiftProject(platformProjectRoot, projectName);

                if (isSwift) {
                    // For Swift projects, add imports to AppDelegate.swift
                    try {
                        const appDelegateSwiftPath = path.join(platformProjectRoot, projectName, 'AppDelegate.swift');
                        // Re-read the file to get the current state (including any changes from previous functions)
                        const appDelegateSwiftContents = await fs.readFile(appDelegateSwiftPath, 'utf-8');

                        // console.log(`appdelegate content hansel before is ${appDelegateSwiftContents}`);

                        // Add Hansel import if Hansel is enabled and not already present (check for exact import line)
                        const lines = appDelegateSwiftContents.split('\n');
                        const hasHanselImport = lines.some(line => line.trim() === swiftImports.hanselImport);
                        if (props.ios.smartechNudges.smartechNudgesEnabled && !hasHanselImport) {
                            let insertIndex = 0;

                            // Find the last import statement
                            for (let i = 0; i < lines.length; i++) {
                                if (lines[i].trim().startsWith('import ')) {
                                    insertIndex = i + 1;
                                } else if (lines[i].trim() !== '' && !lines[i].trim().startsWith('import ')) {
                                    break;
                                }
                            }

                            // Insert the Hansel import
                            lines.splice(insertIndex, 0, swiftImports.hanselImport);
                            const modifiedContents = lines.join('\n');
                            // console.log(`appdelegate content hansel after is ${modifiedContents}`);
                            await fs.writeFile(appDelegateSwiftPath, modifiedContents, 'utf-8');
                            console.log(`${swiftImports.hanselImport} added to AppDelegate.swift at line ${insertIndex + 1}`);
                        } else {
                            console.log(`${swiftImports.hanselImport} is already added or SmartechNudges disabled: ${props.ios.smartechNudges.smartechNudgesEnabled}`);
                        }
                    } catch (error) {
                        console.error('Error modifying AppDelegate.swift:', error);
                    }
                } else {
                    // For Objective-C projects (existing logic)
                    try {
                        const appDelegateHeaderPath = path.join(platformProjectRoot, projectName, appdelegateSnippet.fileName);
                        const appDelegateHeaderContents = await fs.readFile(appDelegateHeaderPath, 'utf-8');
                        // Add Hansel header snippet if Hansel is enabled and not already present
                        if (props.ios.smartechNudges.smartechNudgesEnabled && !appDelegateHeaderContents.includes(appdelegateSnippet.hanselHeaderSnippet)) {
                            const modifiedAppDelegateHeaderContents = `${appDelegateHeaderContents}\n${appdelegateSnippet.hanselHeaderSnippet}`;
                            await fs.writeFile(appDelegateHeaderPath, modifiedAppDelegateHeaderContents, 'utf-8');
                        } else {
                            console.log(`${appdelegateSnippet.hanselHeaderSnippet} is already added  or status of SmartechNudge: ${props.ios.smartechNudges.smartechNudgesEnabled}`);
                        }
                    } catch (error) {
                        console.error('Error modifying AppDelegate.h:', error);
                    }
                }
            }
            return config;
        }
    ]);
};

// Set custom AppDelegate contents modifications
const setCustomAppDelegateContentsMod: ConfigPlugin<SmartechBaseProps> = (config: ExpoConfig, props) => {
    return withDangerousMod(config, [
        'ios',
        async (config) => {
            const { platformProjectRoot, projectName } = config.modRequest;

            if (platformProjectRoot && projectName) {
                const isSwift = await Helper.Ios.isSwiftProject(platformProjectRoot, projectName);

                if (isSwift) {
                    // Handle Swift AppDelegate
                    const appDelegateSwiftPath = path.join(platformProjectRoot, projectName, 'AppDelegate.swift');
                    try {
                        let contents = await fs.readFile(appDelegateSwiftPath, 'utf-8');
                        contents = addSmartechInitCodeSwift(contents, props);
                        contents = addDeepLinkHandlerSwift(contents, props);
                        await fs.writeFile(appDelegateSwiftPath, contents, 'utf-8');
                        console.log('Swift AppDelegate contents updated successfully');
                    } catch (error) {
                        console.error('Error modifying AppDelegate.swift:', error);
                    }
                } else {
                    // Handle Objective-C AppDelegate (existing logic)
                    const appDelegatePath = await Helper.Ios.getObjCAppDelegatePath(platformProjectRoot, projectName);
                    try {
                        let contents = await fs.readFile(appDelegatePath, 'utf-8');
                        const smartechInitContent = addSmartechInitCode(contents, props);
                        const updatedContent = Helper.Ios.addDeepLinkHandler(smartechInitContent, props);
                        await fs.writeFile(appDelegatePath, updatedContent, 'utf-8');
                        console.log(`Objective-C AppDelegate contents updated successfully: ${appDelegatePath}`);
                    } catch (error) {
                        console.error(`Error modifying AppDelegate file (${appDelegatePath}):`, error);
                    }
                }
            }
            return config;
        }
    ]);
};

// Helper function to modify AppDelegate contents
const addSmartechInitCode = (contents: string, props: SmartechBaseProps): string => {
    const lines = contents.split("\n");
    const didLaunchIndex = lines.findIndex(line => smartechBaseSnippet.regexPattern.test(line));

    if (didLaunchIndex === -1) {
        return contents;
    }

    // Prepare the lines to insert
    const sdkLines: string[] = [
        smartechBaseSnippet.smartechInit,
        ...(props.ios.isLogEnabled ? [smartechBaseSnippet.debugLevel] : []),
        smartechBaseSnippet.trackAppInstallUpdateBySmartech,
        ...((props.ios.smartechNudges.smartechNudgesEnabled && props.ios.smartechNudges.isLogEnabled) ? [smartechBaseSnippet.hanselLogSnippet] : []),
    ];

    // Add the snippets before and after the `didFinishLaunching` index
    const resultLines = [
        ...lines.slice(0, didLaunchIndex + 2),
        ...sdkLines,
        ...lines.slice(didLaunchIndex + 2),
    ];

    return resultLines.join("\n");
};

// Helper function to modify Swift AppDelegate contents
const addSmartechInitCodeSwift = (contents: string, props: SmartechBaseProps): string => {
    const lines = contents.split("\n");

    // Find didFinishLaunchingWithOptions method by looking for the specific method name
    let didLaunchIndex = -1;
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        // Look for func application and then check if didFinishLaunchingWithOptions appears in the next few lines
        if (smartechBaseSwiftSnippet.didFinishLaunchingRegex.test(line)) {
            // Check the next 5 lines for didFinishLaunchingWithOptions
            for (let j = i; j < Math.min(i + 5, lines.length); j++) {
                if (lines[j].includes('didFinishLaunchingWithOptions')) {
                    didLaunchIndex = i;
                    break;
                }
            }
            if (didLaunchIndex !== -1) break;
        }
    }

    if (didLaunchIndex === -1) {
        console.log('Could not find didFinishLaunchingWithOptions method in Swift AppDelegate');
        return contents;
    }

    // Find the opening brace of the method
    let methodStartIndex = didLaunchIndex;
    for (let i = didLaunchIndex; i < lines.length; i++) {
        if (lines[i].includes('{')) {
            methodStartIndex = i;
            break;
        }
    }

    // Prepare the lines to insert
    const sdkLines: string[] = [
        `        ${smartechBaseSwiftSnippet.smartechInit}`,
        ...(props.ios.isLogEnabled ? [`        ${smartechBaseSwiftSnippet.debugLevel}`] : []),
        `        ${smartechBaseSwiftSnippet.trackAppInstallUpdateBySmartech}`,
        ...((props.ios.smartechNudges.smartechNudgesEnabled && props.ios.smartechNudges.isLogEnabled) ? [`        ${smartechBaseSwiftSnippet.hanselLogSnippet}`] : []),
    ];

    // Add the snippets after the method opening brace
    const resultLines = [
        ...lines.slice(0, methodStartIndex + 1),
        ...sdkLines,
        ...lines.slice(methodStartIndex + 1),
    ];

    console.log('Swift SDK initialization code added to didFinishLaunchingWithOptions');
    return resultLines.join("\n");
};

// Helper function to add Swift deeplink handler
const addDeepLinkHandlerSwift = (contents: string, props: SmartechBaseProps): string => {
    const lines = contents.split("\n");

    // Find the end of the main AppDelegate class (before the last closing brace, but not the ReactNativeDelegate class)
    let classEndIndex = -1;
    let insideAppDelegate = false;
    let braceCount = 0;

    for (let i = 0; i < lines.length; i++) {
        const line = lines[i].trim();

        // Look for the AppDelegate class declaration
        if (line.includes('class') && line.includes('AppDelegate') && line.includes(':')) {
            insideAppDelegate = true;
            braceCount = 0;
        }

        if (insideAppDelegate) {
            // Count braces to find the end of the AppDelegate class
            const openBraces = (line.match(/\{/g) || []).length;
            const closeBraces = (line.match(/\}/g) || []).length;
            braceCount += openBraces - closeBraces;

            // When we reach the closing brace of AppDelegate class
            if (braceCount === 0 && line.includes('}') && openBraces === 0) {
                classEndIndex = i;
                break;
            }
        }
    }

    if (classEndIndex === -1) {
        console.log('Could not find AppDelegate class end in Swift file');
        return contents;
    }

    const deepLinkHandlerLines = deepLinkHandlerSwiftSnippet(props.deepLinkDelay);

    const resultLines = [
        ...lines.slice(0, classEndIndex),
        deepLinkHandlerLines,
        ...lines.slice(classEndIndex),
    ];

    return resultLines.join("\n");
};

// Helper function to modify Swift openURL method
const modifySwiftOpenURLMethod = (contents: string): string => {
    const swiftCodeBlock = `let handleBySmartech = Smartech.sharedInstance().application(app, open: url, options: options)
    if !handleBySmartech {
      return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
    }
    return true`;

    if (contents.includes("open url:")) {
        const modifiedContents = contents.replace(
            "return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)", swiftCodeBlock);
        return modifiedContents;
    }

    console.log('Could not find Swift openURL method to modify');
    return contents;
};

// Set custom AppDelegate contents modifications
const modifyAppDelegateWithLinker: ConfigPlugin<SmartechBaseProps> = (config: ExpoConfig, props) => {
    const addTestDeviceForSmartech = props.ios.addTestDevice ?? false;
    const addTestDeviceForHansel =
        props.ios.smartechNudges.smartechNudgesEnabled &&
        (props.ios.smartechNudges.addTestDevice ?? false);

    if (!addTestDeviceForSmartech && !addTestDeviceForHansel) {
        return config;
    }

    return withDangerousMod(config, [
        'ios',
        async (config) => {
            const { platformProjectRoot, projectName } = config.modRequest;

            if (platformProjectRoot && projectName) {
                const isSwift = await Helper.Ios.isSwiftProject(platformProjectRoot, projectName);

                if (isSwift) {
                    // Handle Swift AppDelegate
                    const appDelegateSwiftPath = path.join(platformProjectRoot, projectName, 'AppDelegate.swift');
                    try {
                        let contents = await fs.readFile(appDelegateSwiftPath, 'utf-8');
                        contents = modifySwiftOpenURLMethod(contents);
                        await fs.writeFile(appDelegateSwiftPath, contents, 'utf-8');
                        console.log('Swift AppDelegate openURL method updated successfully');
                    } catch (error) {
                        console.error('Error modifying Swift AppDelegate openURL method:', error);
                    }
                } else {
                    // Handle Objective-C AppDelegate (existing logic)
                    const appDelegatePath = await Helper.Ios.getObjCAppDelegatePath(platformProjectRoot, projectName);
                    try {
                        let contents = await fs.readFile(appDelegatePath, 'utf-8');
                        const codeBlock = `BOOL handleBySmartech = [[Smartech sharedInstance] application:application openURL:url options:options];
        if(!handleBySmartech) {
            return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
        }
        return YES;`;
                        if (contents.includes("openURL:")) {
                            const modifiedContents = contents.replace(
                                "return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];", `${codeBlock}`);
                            await fs.writeFile(appDelegatePath, modifiedContents, 'utf-8');
                        }
                    } catch (error) {
                        console.error('Error modifying Objective-C AppDelegate openURL method:', error);
                    }
                }
            }
            return config;
        }
    ]);
};

export const withNetcoreiOS: ConfigPlugin<SmartechBaseProps> = (config, props) => {
    config = modifyEntitlementsPlist(config, props);
    config = modifyInfoPlist(config, props);
    config = addSmartechURLScheme(config, props);
    config = addHanselURLScheme(config, props);
    config = addSmartechHeaderMod(config, props);
    config = addSmartechHanseleHeaderMod(config, props);
    config = setCustomAppDelegateContentsMod(config, props);
    config = modifyAppDelegateWithLinker(config, props);
    return config;
}
