package tech.easyflow.autoconfig.license;

import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;

/**
 * 采集当前机器的授权指纹输入信息。
 */
@Component
public class MachineIdentityCollector {

    private static final Duration COMMAND_TIMEOUT = Duration.ofSeconds(10);

    private final ResourceLoader resourceLoader;
    private final String machineIdFile;
    private final String productUuidFile;
    private final String macAddressFile;

    /**
     * 构造默认机器信息采集器。
     */
    public MachineIdentityCollector() {
        this(new DefaultResourceLoader(), null, null, null);
    }

    /**
     * 构造支持外部文件覆盖的机器信息采集器。
     *
     * @param resourceLoader 资源加载器
     * @param machineIdFile machineId 文件位置
     * @param productUuidFile productUuid 文件位置
     * @param macAddressFile MAC 地址文件位置
     */
    public MachineIdentityCollector(ResourceLoader resourceLoader,
                                    String machineIdFile,
                                    String productUuidFile,
                                    String macAddressFile) {
        this.resourceLoader = resourceLoader;
        this.machineIdFile = machineIdFile;
        this.productUuidFile = productUuidFile;
        this.macAddressFile = macAddressFile;
    }

    /**
     * 采集当前机器的授权标识信息。
     *
     * @return 当前机器标识
     */
    public MachineIdentity collect() {
        String osName = currentOsName().toLowerCase(Locale.ROOT);
        return switch (resolveOsType(osName)) {
            case WINDOWS -> collectWindowsIdentity();
            case MAC -> collectMacIdentity();
            case LINUX -> collectLinuxIdentity();
        };
    }

    /**
     * 获取当前操作系统名称。
     *
     * @return 操作系统名称
     */
    protected String currentOsName() {
        return System.getProperty("os.name", "");
    }

    /**
     * 采集 Linux 机器信息。
     *
     * @return 机器信息
     */
    protected MachineIdentity collectLinuxIdentity() {
        String machineId = readConfiguredValue("读取 Linux machineId 文件", machineIdFile);
        if (!StringUtils.hasText(machineId)) {
            machineId = executeShellCommand("读取 Linux machineId", "cat /etc/machine-id");
        }
        String productUuid = readConfiguredValue("读取 Linux productUuid 文件", productUuidFile);
        if (!StringUtils.hasText(productUuid)) {
            productUuid = executeShellCommand("读取 Linux productUuid", "cat /sys/class/dmi/id/product_uuid");
        }
        String macAddress = readConfiguredValue("读取 Linux MAC 文件", macAddressFile);
        if (!StringUtils.hasText(macAddress)) {
            macAddress = executeShellCommand("读取 Linux 默认网卡 MAC",
                    "cat /sys/class/net/$(ip route | awk '/default/ {print $5; exit}')/address");
        }
        return new MachineIdentity(machineId, productUuid, macAddress);
    }

    /**
     * 采集 macOS 机器信息。
     *
     * @return 机器信息
     */
    protected MachineIdentity collectMacIdentity() {
        String machineId = readConfiguredValue("读取 macOS machineId 文件", machineIdFile);
        if (!StringUtils.hasText(machineId)) {
            machineId = executeShellCommand("读取 macOS machineId",
                    "ioreg -rd1 -c IOPlatformExpertDevice | awk -F'\"' '/IOPlatformUUID/ {print $(NF-1)}'");
        }
        String productUuid = readConfiguredValue("读取 macOS productUuid 文件", productUuidFile);
        if (!StringUtils.hasText(productUuid)) {
            productUuid = executeShellCommand("读取 macOS productUuid",
                    "ioreg -rd1 -c IOPlatformExpertDevice | awk -F'\"' '/IOPlatformUUID/ {print $(NF-1)}'");
        }
        String macAddress = readConfiguredValue("读取 macOS MAC 文件", macAddressFile);
        if (!StringUtils.hasText(macAddress)) {
            macAddress = executeShellCommand("读取 macOS 默认网卡 MAC",
                    "networksetup -listallhardwareports | awk '/Device/ {device=$2} /Ethernet Address/ {print $3; exit}'");
        }
        return new MachineIdentity(machineId, productUuid, macAddress);
    }

    /**
     * 采集 Windows 机器信息。
     *
     * @return 机器信息
     */
    protected MachineIdentity collectWindowsIdentity() {
        String machineId = readConfiguredValue("读取 Windows machineId 文件", machineIdFile);
        if (!StringUtils.hasText(machineId)) {
            machineId = executePowerShellCommand("读取 Windows machineId",
                    "(Get-ItemProperty 'HKLM:\\SOFTWARE\\Microsoft\\Cryptography').MachineGuid");
        }
        String productUuid = readConfiguredValue("读取 Windows productUuid 文件", productUuidFile);
        if (!StringUtils.hasText(productUuid)) {
            productUuid = executePowerShellCommand("读取 Windows productUuid",
                    "(Get-CimInstance Win32_ComputerSystemProduct).UUID");
        }
        String macAddress = readConfiguredValue("读取 Windows MAC 文件", macAddressFile);
        if (!StringUtils.hasText(macAddress)) {
            macAddress = executePowerShellCommand("读取 Windows 默认网卡 MAC",
                    "(Get-NetAdapter | Where-Object {$_.Status -eq 'Up' -and $_.MacAddress} | Select-Object -First 1 -ExpandProperty MacAddress)");
        }
        return new MachineIdentity(machineId, productUuid, macAddress);
    }

    /**
     * 读取外部配置文件中的机器参数。
     *
     * @param description 描述
     * @param location 资源位置
     * @return 文件内容；未配置时返回空字符串
     */
    protected String readConfiguredValue(String description, String location) {
        if (!StringUtils.hasText(location)) {
            return "";
        }
        try {
            Resource resource = resourceLoader.getResource(location);
            if (!resource.exists() || !resource.isReadable()) {
                throw new IllegalStateException(description + "失败，资源不存在或不可读: " + location);
            }
            String output = readStream(resource.getInputStream()).trim();
            if (!StringUtils.hasText(output)) {
                throw new IllegalStateException(description + "失败，输出为空");
            }
            return output;
        } catch (IOException e) {
            throw new IllegalStateException(description + "失败，无法读取资源: " + location, e);
        }
    }

    /**
     * 通过 shell 执行命令。
     *
     * @param description 描述
     * @param command 命令
     * @return 命令输出
     */
    protected String executeShellCommand(String description, String command) {
        return executeCommand(description, List.of("sh", "-lc", command));
    }

    /**
     * 通过 PowerShell 执行命令。
     *
     * @param description 描述
     * @param command 命令
     * @return 命令输出
     */
    protected String executePowerShellCommand(String description, String command) {
        return executeCommand(description, List.of("powershell", "-NoProfile", "-Command", command));
    }

    /**
     * 执行命令并返回文本输出。
     *
     * @param description 描述
     * @param command 命令行
     * @return 命令输出
     */
    protected String executeCommand(String description, List<String> command) {
        Process process = null;
        try {
            process = new ProcessBuilder(command)
                    .redirectErrorStream(true)
                    .start();
            boolean finished = process.waitFor(COMMAND_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
            if (!finished) {
                process.destroyForcibly();
                throw new IllegalStateException(description + "超时");
            }

            String output = readStream(process.getInputStream()).trim();
            if (process.exitValue() != 0) {
                throw new IllegalStateException(description + "失败，退出码=" + process.exitValue() + "，输出=" + output);
            }
            if (!StringUtils.hasText(output)) {
                throw new IllegalStateException(description + "失败，输出为空");
            }
            return output;
        } catch (IOException e) {
            throw new IllegalStateException(description + "失败，无法执行命令", e);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IllegalStateException(description + "失败，命令执行被中断", e);
        } finally {
            if (process != null) {
                process.destroy();
            }
        }
    }

    /**
     * 读取输入流内容。
     *
     * @param inputStream 输入流
     * @return 文本内容
     * @throws IOException 读取失败
     */
    protected String readStream(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        inputStream.transferTo(outputStream);
        return outputStream.toString(StandardCharsets.UTF_8);
    }

    private OsType resolveOsType(String osName) {
        if (osName.contains("win")) {
            return OsType.WINDOWS;
        }
        if (osName.contains("mac") || osName.contains("darwin")) {
            return OsType.MAC;
        }
        if (osName.contains("nux") || osName.contains("linux")) {
            return OsType.LINUX;
        }
        throw new IllegalStateException("不支持的操作系统: " + currentOsName());
    }

    /**
     * 当前操作系统分类。
     */
    private enum OsType {
        WINDOWS,
        MAC,
        LINUX
    }
}
