package net.northking.cctp.upperComputer.deviceManager;

import net.northking.cctp.upperComputer.config.BuildWdaConfig;
import net.northking.cctp.upperComputer.deviceManager.common.PyMobileDevice;
import net.northking.cctp.upperComputer.deviceManager.thread.IosDeviceInitThread;
import net.northking.cctp.upperComputer.deviceManager.thread.MacIosDeviceInitThread;
import net.northking.cctp.upperComputer.deviceManager.thread.WindowsAndLinuxIosDeviceInitThread;
import net.northking.cctp.upperComputer.deviceManager.utils.XcodeBuild;
import net.northking.cctp.upperComputer.driver.usbmuxd.AppleDevice;
import net.northking.cctp.upperComputer.driver.usbmuxd.AppleDeviceWatcher;
import net.northking.cctp.upperComputer.driver.usbmuxd.IOSDeviceListener;
import net.northking.cctp.upperComputer.driver.usbmuxd.UsbMuxd;
import net.northking.cctp.upperComputer.driver.usbmuxd.payload.usbmuxd.entity.DeviceConnectionDetail;
import net.northking.cctp.upperComputer.entity.PhoneEntity;
import net.northking.cctp.upperComputer.utils.SpringUtils;
import net.northking.cctp.upperComputer.webSocket.thread.IosScreenResponseThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class IOSDeviceManager extends AbstractDeviceManager {

    private final Logger logger = LoggerFactory.getLogger(IOSDeviceManager.class);

    private static IOSDeviceManager iosDeviceManager;

    private IOSDeviceListener iosDeviceListener = new IOSDeviceListener();

    public static IOSDeviceManager getInstance() {
        if (IOSDeviceManager.iosDeviceManager == null) {
            IOSDeviceManager.iosDeviceManager = new IOSDeviceManager();
        }
        return IOSDeviceManager.iosDeviceManager;
    }

    private Properties phonePortProperties = new Properties();

    /**
     * 系统默认最新版本的
     */
    public String defaultXcodePath = "/Applications/Xcode.app";

    /**
     * 初始的端口
     */
    private int maxPort = 6000;

    /**
     * 设备wda进程维护线程
     */
    private Map<String, IosDeviceInitThread> onlineIosInitThreadMap = new ConcurrentHashMap<>();

    /**
     * ios推送屏幕
     */
    public ConcurrentHashMap<String, IosScreenResponseThread> screenMap = new ConcurrentHashMap<>();

    /**
     * 上过线的手机设备
     */
    private Map<String, PhoneEntity> phoneMap = new ConcurrentHashMap<>();

    /**
     * 存放需要sessionId来调wda接口的session
     */
    private ConcurrentHashMap<String, String> wdaSessionMap = new ConcurrentHashMap<>();

    /**
     * 上线设备
     */
    private ConcurrentHashMap<String, AppleDevice> appleDeviceMap = new ConcurrentHashMap<>();
    /**
     * 设备序列号与SpecificAppleDeviceInfo对应
     */
    private static final HashMap<String, PyMobileDevice.SpecificAppleDeviceInfo> pyMobileDeviceMap = new HashMap<>();

    private Map<String, String> upDownMap = new ConcurrentHashMap<>();

    private UsbMuxd usbMuxd = new UsbMuxd();

    @Override
    public void run() {
        boolean isMac = UpperComputerManager.getInstance().isMac();
        if (isMac) {
            BuildWdaConfig wdaConfig = SpringUtils.getBean(BuildWdaConfig.class);
            if (wdaConfig.isBuildWda()) {
                //开始构建wda
                logger.debug("mac作为上位机,开始构建wda.................................");
                boolean buildSuccess = startBuildWda(wdaConfig);
                logger.debug("wda构建结束,构建结果：{}.................................", buildSuccess);
            } else {
                logger.debug("mac作为上位机,不构建wda.................................");
                setXcodeRequireParam(wdaConfig);
            }
        }
        readIosPhoneProperty();
        //添加监听器
        AppleDeviceWatcher.getInstance().addOnDeviceChangeListener(iosDeviceListener);
        AppleDeviceWatcher.getInstance().start();
    }

    private void setXcodeRequireParam(BuildWdaConfig wdaConfig) {
        /**
         * 先用xcode14打包ios17以下的，然后再切换到最新的xcode，为了后面直接用最新版的xcode启动wda
         */
        XcodeBuild xcodeBuild = XcodeBuild.getInstance();
        xcodeBuild.setXcodeUserName(wdaConfig.getXcodeUsername());
        xcodeBuild.setWebDriverAgentProjectPath(wdaConfig.getWdaProjectPath());
        xcodeBuild.setSdk16XctestPath(wdaConfig.getWdaForIos17BelowPackageDefaultPath());
        xcodeBuild.setDefaultXctestPath(wdaConfig.getWdaForIos17AbovePackageDefaultPath());
        xcodeBuild.setLogPath(UpperComputerManager.getInstance().getApplicationPath()+"/wdaLog/");
    }

    private boolean startBuildWda(BuildWdaConfig wdaConfig) {
        /**
         * 先用xcode14打包ios17以下的，然后再切换到最新的xcode，为了后面直接用最新版的xcode启动wda
         */
        XcodeBuild xcodeBuild = XcodeBuild.getInstance();
        xcodeBuild.setXcodeUserName(wdaConfig.getXcodeUsername());
        xcodeBuild.setWebDriverAgentProjectPath(wdaConfig.getWdaProjectPath());
        xcodeBuild.setLogPath(UpperComputerManager.getInstance().getApplicationPath()+"/wdaLog/");
        //ios17以下使用
        if (!xcodeBuild.switchXcodeVersion(wdaConfig.getXcode14Path())) {
            logger.error("构建所有WDA时，切换到Xcode14失败");
            return false;
        }
        String sdk16XctestRunPath = xcodeBuild.buildWda(wdaConfig.getKeyChainPassword(), wdaConfig.getWdaForIos17BelowPackagePath());
        if (sdk16XctestRunPath == null) {
            logger.error("构建所有WDA时，SDK16版本WDA构建失败");
            return false;
        }
        xcodeBuild.setSdk16XctestPath(sdk16XctestRunPath);
        logger.info("ios17以下的wda构建完成........................................");
        if (!xcodeBuild.switchXcodeVersion(defaultXcodePath)) {
            logger.error("构建所有WDA时，切换到默认版本Xcode失败");
            return false;
        }
        //ios17及以上使用
        String defaultXctestRunPath = xcodeBuild.buildWda(wdaConfig.getKeyChainPassword(), wdaConfig.getWdaForIos17AbovePackagePath());
        if (defaultXctestRunPath == null) {
            logger.error("构建所有WDA时，默认版本WDA构建失败");
            return false;
        }
        xcodeBuild.setDefaultXctestPath(defaultXctestRunPath);
        logger.info("ios17以上的wda构建完成........................................");
        return true;
    }


    public void saveDeviceScreenThread(String deviceId,IosScreenResponseThread screenResponseThread){
        this.screenMap.put(deviceId, screenResponseThread);
    }

    public IosScreenResponseThread getScreenThread(String deviceId){
        return this.screenMap.get(deviceId);
    }

    public IosScreenResponseThread removeScreenThread(String deviceId){
        return this.screenMap.remove(deviceId);
    }

    private void readIosPhoneProperty() {
        String property = System.getProperty("user.dir");
        File file = new File(property + "/phonePort.properties");
        logger.info("开始读取已经存在的手机端口和wda端口映射，读取文件：{}", file.getAbsolutePath());
        if (!file.exists()) {
            logger.warn("手机端口映射文件不存在，新建《phonePort.properties》文件");
            try {
                boolean newFile = file.createNewFile();
                if (!newFile) {
                    logger.warn("端口映射文件【{}】创建失败", file.getAbsolutePath());
                }
            } catch (IOException e) {
                logger.warn("新建端口映射文件失败", e);
            }
        } else {
            FileInputStream inputStream = null;
            try {
                inputStream = new FileInputStream(file);
                phonePortProperties.load(inputStream);
            } catch (FileNotFoundException e) {
                logger.warn("手机端口映射文件不存在");
            } catch (IOException e) {
                logger.warn("读取手机端口映射配置文件失败");
            } finally {
                if (null != inputStream) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        logger.warn("关闭流出错", e);
                    }
                }
            }
            logger.info("手机端口映射文件加载完成，加载内容：");
            for (Object key : phonePortProperties.keySet()) {
                String port = phonePortProperties.getProperty(key + "");
                PhoneEntity entity = new PhoneEntity();
                entity.setUdid(key + "");
                int local8100Port = Integer.parseInt(port);
                entity.setPort8100(local8100Port);
                entity.setStatus(false);  //默认不在线
                phoneMap.put(key + "", entity);
                logger.info("手机:{},8100端口映射:{}", key, port);
                if (maxPort < local8100Port) {
                    maxPort = local8100Port;
                }
            }
        }
    }





    /**
     * 拿最大新设备的代理端口
     *
     * @return
     */
    private synchronized int getMaxPort() {
        this.maxPort++;
        return this.maxPort;
    }

    /**
     * 拿已经上过线的手机
     *
     * @param udid
     * @return
     */
    public PhoneEntity getPhoneEntity(String udid) {
        return this.phoneMap.get(udid);
    }

    /**
     * 持久化上过线的手机代理端口
     *
     * @param key
     * @param value
     */
    private synchronized void doPropertiesWrite(String key, String value) {
        this.phonePortProperties.setProperty(key, value);  //存好分配的端口
        String property = System.getProperty("user.dir");
        File file = new File(property + "/phonePort.properties");
        try {
            this.phonePortProperties.store(new FileOutputStream(file), "IOS设备WDA映射关系");
        } catch (IOException e) {
            logger.warn("写入端口映射文件失败");
        }
    }

    public IosDeviceInitThread getIosInitThread(String uuid) {
        return this.onlineIosInitThreadMap.get(uuid);
    }

    @Override
    public void shutdown() {
        AppleDeviceWatcher.getInstance().removeOnDeviceChangeListener(iosDeviceListener);
        AppleDeviceWatcher.getInstance().stopWatch();
        //清除存在的设备信息
        phoneMap.clear();
        for (Map.Entry<String, IosDeviceInitThread> entry : onlineIosInitThreadMap.entrySet()) {
            IosDeviceInitThread initThread = entry.getValue();
            if (!initThread.isInterrupted() && initThread.isAlive()) {
                initThread.exitWdaAndProxy();
            }
        }
        pyMobileDeviceMap.clear();
        onlineIosInitThreadMap.clear();
        wdaSessionMap.clear();
        interrupt();
    }

    public boolean checkMobileIsOnline(String serial) {
        PhoneEntity entity = this.phoneMap.get(serial);
        if (null != entity && entity.getStatus()) {
            return true;
        }
        return false;
    }

    public void handleIosDevice(DeviceConnectionDetail deviceConnectionDetail) {
        //上线
        if (deviceConnectionDetail.connectStatus == DeviceConnectionDetail.ConnectStatus.ATTACHED) {
            onlineIosDevice(deviceConnectionDetail);
        } else {  //下线
            offlineIosDevice(deviceConnectionDetail);
        }
    }

    private void offlineIosDevice(DeviceConnectionDetail deviceConnectionDetail) {
        String serialNumber = upDownMap.remove(deviceConnectionDetail.deviceId+"");
        logger.warn("ios设备【{}】掉线了，准备清除有关进程。。。。。。", serialNumber);
        PhoneEntity phoneEntity = this.phoneMap.get(serialNumber);
        if (null != phoneEntity) {
            IosDeviceInitThread initThread = this.onlineIosInitThreadMap.remove(serialNumber);
                if (null != initThread && initThread.isAlive() && !initThread.isInterrupted()) {
                    initThread.exitWdaAndProxy();
                }
            phoneEntity.setStatus(false);
            offlineDeviceFromDeviceManager(phoneEntity.getUdid());
            publishDeviceOfflineToWebConnection(serialNumber);
        } else {
            logger.warn("无此设备【{}】记录。。。。。。。。。。。。", serialNumber);
        }
        PyMobileDevice.SpecificAppleDeviceInfo specificAppleDeviceInfo = pyMobileDeviceMap.remove(serialNumber);
        wdaSessionMap.remove(serialNumber);  //清除wdaSession

    }


    private void onlineIosDevice(DeviceConnectionDetail deviceConnectionDetail) {
        PhoneEntity phoneEntity = this.phoneMap.get(deviceConnectionDetail.serialNumber);
        if (null == phoneEntity) {  //从未上过线
            logger.info("检测到有ios设备【{}】上线了,此设备第一次来。。。。。。。。。。。。。", deviceConnectionDetail.serialNumber);
            phoneEntity = new PhoneEntity();
            phoneEntity.setUdid(deviceConnectionDetail.serialNumber);
            phoneEntity.setPort8100(getMaxPort());
            doPropertiesWrite(phoneEntity.getUdid(), phoneEntity.getPort8100() + "");
            this.phoneMap.put(phoneEntity.getUdid(), phoneEntity);
        } else {
            logger.info("检测到有ios设备【{}】上线了,此设备是熟客了。。。。。。。。。。。。。", deviceConnectionDetail.serialNumber);
        }
        //读取设备的版本信息
        AppleDevice device = new AppleDevice(deviceConnectionDetail);
        boolean result = usbMuxd.fullDeviceInfo(device);
        if (!result) {
            logger.error("填充新的苹果设备【{}】信息失败",deviceConnectionDetail.serialNumber);
            return;
        }
        phoneEntity.setProductVersion(device.getProductVersion());
        boolean isMac = UpperComputerManager.getInstance().getOperatingSystem().startsWith("Mac");
        /**
         * 如果是mac作为上位机，还需要创建SpecificAppleDeviceInfo
         */
        if (isMac) {
            logger.debug("mac作为上位机，开始为设备【{}】创建SpecificAppleDeviceInfo",deviceConnectionDetail.serialNumber);
            synchronized (pyMobileDeviceMap) {
                if (!pyMobileDeviceMap.containsKey(deviceConnectionDetail.serialNumber)) {
                    PyMobileDevice.SpecificAppleDeviceInfo spAppleDeviceInfo = new PyMobileDevice.SpecificAppleDeviceInfo(device);
                    result = PyMobileDevice.getInstance().createDeviceTunnel(spAppleDeviceInfo);
                    if (result) {
                        pyMobileDeviceMap.put(deviceConnectionDetail.serialNumber, spAppleDeviceInfo);
                        logger.debug("设备【{}】创建SpecificAppleDeviceInfo成功.....................", deviceConnectionDetail.serialNumber);
                    } else {
                        logger.debug("设备【{}】创建SpecificAppleDeviceInfo失败.....................", deviceConnectionDetail.serialNumber);
                        return;
                    }
                } else {
                    logger.debug("设备【{}】已经存在SpecificAppleDeviceInfo，无需再次创建.....................", deviceConnectionDetail.serialNumber);
                    return;
                }
            }

        }
        /**
         * 启动wda
         */
        IosDeviceInitThread iosDeviceInitThread = this.onlineIosInitThreadMap.get(deviceConnectionDetail.serialNumber);
        if (null == iosDeviceInitThread || !iosDeviceInitThread.isAlive() || iosDeviceInitThread.isInterrupted()) {
            IosDeviceInitThread initThread = null;
            if (isMac) {
                initThread = new MacIosDeviceInitThread(phoneEntity,device);
            } else {
                initThread = new WindowsAndLinuxIosDeviceInitThread(phoneEntity,device);
            }
            initThread.start();
            this.onlineIosInitThreadMap.put(deviceConnectionDetail.serialNumber, initThread);
        }
        this.upDownMap.put(deviceConnectionDetail.deviceId+"", deviceConnectionDetail.serialNumber);
    }

    @Override
    public List<String> getOnlineDevice() {
        List<String> list = new ArrayList<>();
        for (Map.Entry<String, PhoneEntity> entry : phoneMap.entrySet()) {
            PhoneEntity phoneEntity = entry.getValue();
            if (phoneEntity.getStatus()) {
                list.add(phoneEntity.getUdid());
            }
        }
        return list;
    }

    public void addPhoneSessionId(String udid, String sessionId) {
        wdaSessionMap.put(udid, sessionId);
    }

    public String getPhoneWdaSessionId(String udid) {
        return wdaSessionMap.get(udid);
    }

    public PyMobileDevice.SpecificAppleDeviceInfo getSpecificAppleDeviceInfo(String serial){
        return pyMobileDeviceMap.get(serial);
    }
}
