package com.ruoyi.generator.service.front;

import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RunPreviewProcessKiller
{
    private static final long GRACEFUL_WAIT_MILLIS = 1500L;

    public void stop(Process process, Integer port)
    {
        stopProcess(process);
        if (port != null)
        {
            killListeningProcesses(port.intValue());
        }
    }

    private void stopProcess(Process process)
    {
        if (process == null)
        {
            return;
        }
        ManagedPreviewProcess managed = process instanceof ManagedPreviewProcess
                ? (ManagedPreviewProcess) process
                : null;
        try
        {
            if (!process.isAlive())
            {
                return;
            }
            if (managed != null)
            {
                long pid = managed.waitForRootPid(GRACEFUL_WAIT_MILLIS);
                if (pid > 0L)
                {
                    killWindowsProcessTree(pid);
                    waitForExit(process, GRACEFUL_WAIT_MILLIS);
                }
            }
            if (process.isAlive())
            {
                process.destroy();
                waitForExit(process, GRACEFUL_WAIT_MILLIS);
            }
            if (process.isAlive())
            {
                process.destroyForcibly();
                waitForExit(process, GRACEFUL_WAIT_MILLIS);
            }
        }
        finally
        {
            if (managed != null)
            {
                managed.deletePidFile();
            }
        }
    }

    protected boolean killWindowsProcessTree(long pid)
    {
        return executeQuietly(Arrays.asList("taskkill", "/PID", String.valueOf(pid), "/T", "/F"));
    }

    private void killListeningProcesses(int port)
    {
        if (isWindows())
        {
            killWindowsListeningProcesses(port);
        }
        else
        {
            executeQuietly(Arrays.asList("fuser", "-k", port + "/tcp"));
        }
    }

    private void killWindowsListeningProcesses(int port)
    {
        for (Integer pid : findWindowsListeningPids(port))
        {
            killWindowsProcessTree(pid.longValue());
        }
    }

    private List<Integer> findWindowsListeningPids(int port)
    {
        Set<Integer> pids = new LinkedHashSet<Integer>();
        Process netstat = null;
        BufferedReader reader = null;
        try
        {
            netstat = new ProcessBuilder("netstat", "-ano", "-p", "tcp").redirectErrorStream(true).start();
            reader = new BufferedReader(new InputStreamReader(netstat.getInputStream(), Charset.defaultCharset()));
            String line;
            while ((line = reader.readLine()) != null)
            {
                Integer pid = parseWindowsListeningPid(line, port);
                if (pid != null)
                {
                    pids.add(pid);
                }
            }
            waitForExit(netstat, GRACEFUL_WAIT_MILLIS);
        }
        catch (IOException ignored)
        {
            // Best-effort cleanup: process.destroy() above is still applied.
        }
        finally
        {
            closeQuietly(reader);
            if (netstat != null && netstat.isAlive())
            {
                netstat.destroyForcibly();
            }
        }
        return new ArrayList<Integer>(pids);
    }

    private Integer parseWindowsListeningPid(String line, int port)
    {
        String trimmed = line == null ? "" : line.trim();
        if (!trimmed.startsWith("TCP"))
        {
            return null;
        }
        String[] parts = trimmed.split("\\s+");
        if (parts.length < 5 || !"LISTENING".equalsIgnoreCase(parts[3]) || !matchesLocalPort(parts[1], port))
        {
            return null;
        }
        try
        {
            return Integer.valueOf(parts[4]);
        }
        catch (NumberFormatException ignored)
        {
            return null;
        }
    }

    private boolean matchesLocalPort(String localAddress, int port)
    {
        int separator = localAddress == null ? -1 : localAddress.lastIndexOf(':');
        return separator >= 0 && String.valueOf(port).equals(localAddress.substring(separator + 1));
    }

    private boolean executeQuietly(List<String> command)
    {
        Process process = null;
        try
        {
            process = new ProcessBuilder(command).redirectErrorStream(true).start();
            if (!process.waitFor(GRACEFUL_WAIT_MILLIS, TimeUnit.MILLISECONDS))
            {
                return false;
            }
            return process.exitValue() == 0;
        }
        catch (IOException ignored)
        {
            // Missing OS tools should not break the stop API.
            return false;
        }
        catch (InterruptedException e)
        {
            Thread.currentThread().interrupt();
            return false;
        }
        finally
        {
            if (process != null && process.isAlive())
            {
                process.destroyForcibly();
            }
        }
    }

    private void waitForExit(Process process, long timeoutMillis)
    {
        try
        {
            process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS);
        }
        catch (InterruptedException e)
        {
            Thread.currentThread().interrupt();
        }
    }

    private void closeQuietly(BufferedReader reader)
    {
        if (reader == null)
        {
            return;
        }
        try
        {
            reader.close();
        }
        catch (IOException ignored)
        {
        }
    }

    private boolean isWindows()
    {
        String osName = System.getProperty("os.name");
        return osName != null && osName.toLowerCase().contains("win");
    }
}
