import type { ActionFunctionArgs, LoaderFunction } from '@remix-run/node';
import { json } from '@remix-run/node';
import { createScopedLogger } from '~/lib/.server/logger';

// Only import child_process if we're not in a Cloudflare environment
let execSync: any;

const logger = createScopedLogger('api.system.process-info');

try {
  // Check if we're in a Node.js environment
  if (typeof process !== 'undefined' && process.platform) {
    // Using dynamic import to avoid require()
    const childProcess = { execSync: null };
    execSync = childProcess.execSync;
  }
} catch {
  // In Cloudflare environment, this will fail, which is expected
  logger.info('Running in Cloudflare environment, child_process not available');
}

// For development environments, we'll always provide mock data if real data isn't available
const isDevelopment = process.env.NODE_ENV === 'development';

interface ProcessInfo {
  pid: number;
  name: string;
  cpu: number;
  memory: number;
  command?: string;
  timestamp: string;
  error?: string;
}

const getProcessInfo = (): ProcessInfo[] => {
  try {
    // If we're in a Cloudflare environment and not in development, return error
    if (!execSync && !isDevelopment) {
      return [
        {
          pid: 0,
          name: 'N/A',
          cpu: 0,
          memory: 0,
          timestamp: new Date().toISOString(),
          error: 'Process information is not available in this environment',
        },
      ];
    }

    // If we're in development but not in Node environment, return mock data
    if (!execSync && isDevelopment) {
      return getMockProcessInfo();
    }

    // Different commands for different operating systems
    const platform = process.platform;
    let processes: ProcessInfo[] = [];

    // Get CPU count for normalizing CPU percentages
    let cpuCount = 1;

    try {
      if (platform === 'darwin') {
        const cpuInfo = execSync('sysctl -n hw.ncpu', { encoding: 'utf-8' }).toString().trim();
        cpuCount = parseInt(cpuInfo, 10) || 1;
      } else if (platform === 'linux') {
        const cpuInfo = execSync('nproc', { encoding: 'utf-8' }).toString().trim();
        cpuCount = parseInt(cpuInfo, 10) || 1;
      } else if (platform === 'win32') {
        const cpuInfo = execSync('wmic cpu get NumberOfCores', { encoding: 'utf-8' }).toString().trim();
        const match = cpuInfo.match(/\d+/);
        cpuCount = match ? parseInt(match[0], 10) : 1;
      }
    } catch (error) {
      logger.error('Failed to get CPU count:', error);

      // Default to 1 if we can't get the count
      cpuCount = 1;
    }

    if (platform === 'darwin') {
      // macOS - use ps command to get process information
      try {
        const output = execSync('ps -eo pid,pcpu,pmem,comm -r | head -n 11', { encoding: 'utf-8' }).toString().trim();

        // Skip the header line
        const lines = output.split('\n').slice(1);

        processes = lines.map((line: string) => {
          const parts = line.trim().split(/\s+/);
          const pid = parseInt(parts[0], 10);

          /*
           * Normalize CPU percentage by dividing by CPU count
           * This converts from "% of all CPUs" to "% of one CPU"
           */
          const cpu = parseFloat(parts[1]) / cpuCount;
          const memory = parseFloat(parts[2]);
          const command = parts.slice(3).join(' ');

          return {
            pid,
            name: command.split('/').pop() || command,
            cpu,
            memory,
            command,
            timestamp: new Date().toISOString(),
          };
        });
      } catch (error) {
        logger.error('Failed to get macOS process info:', error);

        // Try alternative command
        try {
          const output = execSync('top -l 1 -stats pid,cpu,mem,command -n 10', { encoding: 'utf-8' }).toString().trim();

          // Parse top output - skip the first few lines of header
          const lines = output.split('\n').slice(6);

          processes = lines.map((line: string) => {
            const parts = line.trim().split(/\s+/);
            const pid = parseInt(parts[0], 10);
            const cpu = parseFloat(parts[1]);
            const memory = parseFloat(parts[2]);
            const command = parts.slice(3).join(' ');

            return {
              pid,
              name: command.split('/').pop() || command,
              cpu,
              memory,
              command,
              timestamp: new Date().toISOString(),
            };
          });
        } catch (fallbackError) {
          logger.error('Failed to get macOS process info with fallback:', fallbackError);
          return [
            {
              pid: 0,
              name: 'N/A',
              cpu: 0,
              memory: 0,
              timestamp: new Date().toISOString(),
              error: 'Process information is not available in this environment',
            },
          ];
        }
      }
    } else if (platform === 'linux') {
      // Linux - use ps command to get process information
      try {
        const output = execSync('ps -eo pid,pcpu,pmem,comm --sort=-pmem | head -n 11', { encoding: 'utf-8' })
          .toString()
          .trim();

        // Skip the header line
        const lines = output.split('\n').slice(1);

        processes = lines.map((line: string) => {
          const parts = line.trim().split(/\s+/);
          const pid = parseInt(parts[0], 10);

          // Normalize CPU percentage by dividing by CPU count
          const cpu = parseFloat(parts[1]) / cpuCount;
          const memory = parseFloat(parts[2]);
          const command = parts.slice(3).join(' ');

          return {
            pid,
            name: command.split('/').pop() || command,
            cpu,
            memory,
            command,
            timestamp: new Date().toISOString(),
          };
        });
      } catch (error) {
        logger.error('Failed to get Linux process info:', error);

        // Try alternative command
        try {
          const output = execSync('top -b -n 1 | head -n 17', { encoding: 'utf-8' }).toString().trim();

          // Parse top output - skip the first few lines of header
          const lines = output.split('\n').slice(7);

          processes = lines.map((line: string) => {
            const parts = line.trim().split(/\s+/);
            const pid = parseInt(parts[0], 10);
            const cpu = parseFloat(parts[8]);
            const memory = parseFloat(parts[9]);
            const command = parts[11] || parts[parts.length - 1];

            return {
              pid,
              name: command.split('/').pop() || command,
              cpu,
              memory,
              command,
              timestamp: new Date().toISOString(),
            };
          });
        } catch (fallbackError) {
          logger.error('Failed to get Linux process info with fallback:', fallbackError);
          return [
            {
              pid: 0,
              name: 'N/A',
              cpu: 0,
              memory: 0,
              timestamp: new Date().toISOString(),
              error: 'Process information is not available in this environment',
            },
          ];
        }
      }
    } else if (platform === 'win32') {
      // Windows - use PowerShell to get process information
      try {
        const output = execSync(
          'powershell "Get-Process | Sort-Object -Property WorkingSet64 -Descending | Select-Object -First 10 Id, CPU, @{Name=\'Memory\';Expression={$_.WorkingSet64/1MB}}, ProcessName | ConvertTo-Json"',
          { encoding: 'utf-8' },
        )
          .toString()
          .trim();

        const processData = JSON.parse(output);
        const processArray = Array.isArray(processData) ? processData : [processData];

        processes = processArray.map((proc: any) => ({
          pid: proc.Id,
          name: proc.ProcessName,

          // Normalize CPU percentage by dividing by CPU count
          cpu: (proc.CPU || 0) / cpuCount,
          memory: proc.Memory,
          timestamp: new Date().toISOString(),
        }));
      } catch (error) {
        logger.error('Failed to get Windows process info:', error);

        // Try alternative command using tasklist
        try {
          const output = execSync('tasklist /FO CSV', { encoding: 'utf-8' }).toString().trim();

          // Parse CSV output - skip the header line
          const lines = output.split('\n').slice(1);

          processes = lines.slice(0, 10).map((line: string) => {
            // Parse CSV format
            const parts = line.split(',').map((part: string) => part.replace(/^"(.+)"$/, '$1'));
            const pid = parseInt(parts[1], 10);
            const memoryStr = parts[4].replace(/[^\d]/g, '');
            const memory = parseInt(memoryStr, 10) / 1024; // Convert KB to MB

            return {
              pid,
              name: parts[0],
              cpu: 0, // tasklist doesn't provide CPU info
              memory,
              timestamp: new Date().toISOString(),
            };
          });
        } catch (fallbackError) {
          logger.error('Failed to get Windows process info with fallback:', fallbackError);
          return [
            {
              pid: 0,
              name: 'N/A',
              cpu: 0,
              memory: 0,
              timestamp: new Date().toISOString(),
              error: 'Process information is not available in this environment',
            },
          ];
        }
      }
    } else {
      logger.warn(`Unsupported platform: ${platform}, using browser fallback`);
      return [
        {
          pid: 0,
          name: 'N/A',
          cpu: 0,
          memory: 0,
          timestamp: new Date().toISOString(),
          error: 'Process information is not available in this environment',
        },
      ];
    }

    return processes;
  } catch (error) {
    logger.error('Failed to get process info:', error);

    if (isDevelopment) {
      return getMockProcessInfo();
    }

    return [
      {
        pid: 0,
        name: 'N/A',
        cpu: 0,
        memory: 0,
        timestamp: new Date().toISOString(),
        error: 'Process information is not available in this environment',
      },
    ];
  }
};

// Generate mock process information with realistic values
const getMockProcessInfo = (): ProcessInfo[] => {
  const timestamp = new Date().toISOString();

  // Create some random variation in CPU usage
  const randomCPU = () => Math.floor(Math.random() * 15);
  const randomHighCPU = () => 15 + Math.floor(Math.random() * 25);

  // Create some random variation in memory usage
  const randomMem = () => Math.floor(Math.random() * 5);
  const randomHighMem = () => 5 + Math.floor(Math.random() * 15);

  return [
    {
      pid: 1,
      name: 'Browser',
      cpu: randomHighCPU(),
      memory: 25 + randomMem(),
      command: 'Browser Process',
      timestamp,
    },
    {
      pid: 2,
      name: 'System',
      cpu: 5 + randomCPU(),
      memory: 10 + randomMem(),
      command: 'System Process',
      timestamp,
    },
    {
      pid: 3,
      name: 'upage',
      cpu: randomHighCPU(),
      memory: 15 + randomMem(),
      command: 'UPage AI Process',
      timestamp,
    },
    {
      pid: 4,
      name: 'node',
      cpu: randomCPU(),
      memory: randomHighMem(),
      command: 'Node.js Process',
      timestamp,
    },
    {
      pid: 5,
      name: 'wrangler',
      cpu: randomCPU(),
      memory: randomMem(),
      command: 'Wrangler Process',
      timestamp,
    },
    {
      pid: 6,
      name: 'vscode',
      cpu: randomCPU(),
      memory: 12 + randomMem(),
      command: 'VS Code Process',
      timestamp,
    },
    {
      pid: 7,
      name: 'chrome',
      cpu: randomHighCPU(),
      memory: 20 + randomMem(),
      command: 'Chrome Browser',
      timestamp,
    },
    {
      pid: 8,
      name: 'finder',
      cpu: 1 + randomCPU(),
      memory: 3 + randomMem(),
      command: 'Finder Process',
      timestamp,
    },
    {
      pid: 10,
      name: 'cloudflared',
      cpu: randomCPU(),
      memory: randomMem(),
      command: 'Cloudflare Tunnel',
      timestamp,
    },
  ];
};

export const processLoader: LoaderFunction = async ({ request: _request }) => {
  try {
    return json(getProcessInfo());
  } catch (error) {
    logger.error('Failed to get process info:', error);
    return json(getMockProcessInfo(), { status: 500 });
  }
};

export const processAction = async ({ request: _request }: ActionFunctionArgs) => {
  try {
    return json(getProcessInfo());
  } catch (error) {
    logger.error('Failed to get process info:', error);
    return json(getMockProcessInfo(), { status: 500 });
  }
};
