import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import process from 'node:process';
import { findChromeExecutable, CHROME_CANDIDATES_FULL, getDefaultProfileDir } from './x-utils.js';

interface CheckResult {
  name: string;
  ok: boolean;
  detail: string;
}

const results: CheckResult[] = [];

function log(label: string, ok: boolean, detail: string): void {
  results.push({ name: label, ok, detail });
  const icon = ok ? '✅' : '❌';
  console.log(`${icon} ${label}: ${detail}`);
}

function warn(label: string, detail: string): void {
  results.push({ name: label, ok: true, detail });
  console.log(`⚠️  ${label}: ${detail}`);
}

async function checkChrome(): Promise<void> {
  const chromePath = findChromeExecutable(CHROME_CANDIDATES_FULL);
  if (chromePath) {
    log('Chrome', true, chromePath);
  } else {
    log('Chrome', false, 'Not found. Set X_BROWSER_CHROME_PATH env var or install Chrome.');
  }
}

async function checkProfileIsolation(): Promise<void> {
  const profileDir = getDefaultProfileDir();
  const userChromeDir = process.platform === 'darwin'
    ? path.join(os.homedir(), 'Library', 'Application Support', 'Google', 'Chrome')
    : process.platform === 'win32'
      ? path.join(os.homedir(), 'AppData', 'Local', 'Google', 'Chrome', 'User Data')
      : path.join(os.homedir(), '.config', 'google-chrome');

  const isIsolated = !profileDir.startsWith(userChromeDir);
  log('Profile isolation', isIsolated, `Skill profile: ${profileDir}`);

  if (isIsolated) {
    const exists = fs.existsSync(profileDir);
    if (exists) {
      log('Profile dir', true, 'Exists and accessible');
    } else {
      try {
        fs.mkdirSync(profileDir, { recursive: true });
        log('Profile dir', true, 'Created successfully');
      } catch (e) {
        log('Profile dir', false, `Cannot create: ${e instanceof Error ? e.message : String(e)}`);
      }
    }
  }
}

async function checkAccessibility(): Promise<void> {
  if (process.platform !== 'darwin') {
    log('Accessibility', true, `Skipped (not macOS, platform: ${process.platform})`);
    return;
  }

  const result = spawnSync('osascript', ['-e', `
    tell application "System Events"
      set frontApp to name of first application process whose frontmost is true
      return frontApp
    end tell
  `], { stdio: 'pipe', timeout: 10_000 });

  if (result.status === 0) {
    const app = result.stdout?.toString().trim();
    log('Accessibility (System Events)', true, `Frontmost app: ${app}`);
  } else {
    const stderr = result.stderr?.toString().trim() || '';
    if (stderr.includes('not allowed assistive access') || stderr.includes('1002')) {
      log('Accessibility (System Events)', false,
        'Denied. Grant access: System Settings → Privacy & Security → Accessibility → enable your terminal app');
    } else {
      log('Accessibility (System Events)', false, `Failed: ${stderr}`);
    }
  }
}

async function checkClipboardCopy(): Promise<void> {
  if (process.platform !== 'darwin') {
    log('Clipboard copy (image)', true, `Skipped (not macOS)`);
    return;
  }

  const tmpDir = await mkdtemp(path.join(os.tmpdir(), 'x-check-'));
  try {
    const testPng = path.join(tmpDir, 'test.png');
    const swiftSrc = `import AppKit
import Foundation
let size = NSSize(width: 2, height: 2)
let image = NSImage(size: size)
image.lockFocus()
NSColor.red.set()
NSBezierPath.fill(NSRect(origin: .zero, size: size))
image.unlockFocus()
guard let tiff = image.tiffRepresentation,
      let rep = NSBitmapImageRep(data: tiff),
      let png = rep.representation(using: .png, properties: [:]) else {
  FileHandle.standardError.write("Failed to create test PNG\\n".data(using: .utf8)!)
  exit(1)
}
try png.write(to: URL(fileURLWithPath: CommandLine.arguments[1]))
`;
    const genScript = path.join(tmpDir, 'gen.swift');
    await writeFile(genScript, swiftSrc, 'utf8');
    const genResult = spawnSync('swift', [genScript, testPng], { stdio: 'pipe', timeout: 30_000 });
    if (genResult.status !== 0) {
      log('Clipboard copy (image)', false, `Cannot create test image: ${genResult.stderr?.toString().trim()}`);
      return;
    }

    const clipSrc = `import AppKit
import Foundation
guard let image = NSImage(contentsOfFile: CommandLine.arguments[1]) else {
  FileHandle.standardError.write("Failed to load image\\n".data(using: .utf8)!)
  exit(1)
}
let pb = NSPasteboard.general
pb.clearContents()
if !pb.writeObjects([image]) {
  FileHandle.standardError.write("Failed to write to clipboard\\n".data(using: .utf8)!)
  exit(1)
}
`;
    const clipScript = path.join(tmpDir, 'clip.swift');
    await writeFile(clipScript, clipSrc, 'utf8');
    const clipResult = spawnSync('swift', [clipScript, testPng], { stdio: 'pipe', timeout: 30_000 });
    if (clipResult.status === 0) {
      log('Clipboard copy (image)', true, 'Can copy image to clipboard via Swift/AppKit');
    } else {
      log('Clipboard copy (image)', false, `Failed: ${clipResult.stderr?.toString().trim()}`);
    }
  } finally {
    await rm(tmpDir, { recursive: true, force: true });
  }
}

async function checkPasteKeystroke(): Promise<void> {
  if (process.platform === 'darwin') {
    const result = spawnSync('osascript', ['-e', `
      tell application "System Events"
        -- Dry run: just check we CAN query key sending capability
        set canSend to true
        return canSend
      end tell
    `], { stdio: 'pipe', timeout: 10_000 });

    if (result.status === 0) {
      log('Paste keystroke (osascript)', true, 'System Events can send keystrokes');
    } else {
      const stderr = result.stderr?.toString().trim() || '';
      log('Paste keystroke (osascript)', false, `Cannot send keystrokes: ${stderr}`);
    }
  } else if (process.platform === 'linux') {
    const xdotool = spawnSync('which', ['xdotool'], { stdio: 'pipe' });
    const ydotool = spawnSync('which', ['ydotool'], { stdio: 'pipe' });
    if (xdotool.status === 0) {
      log('Paste keystroke', true, 'xdotool available (X11)');
    } else if (ydotool.status === 0) {
      log('Paste keystroke', true, 'ydotool available (Wayland)');
    } else {
      log('Paste keystroke', false, 'No tool found. Install xdotool (X11) or ydotool (Wayland).');
    }
  } else if (process.platform === 'win32') {
    log('Paste keystroke', true, 'Windows uses PowerShell SendKeys (built-in)');
  }
}

async function checkBun(): Promise<void> {
  const result = spawnSync('npx', ['-y', 'bun', '--version'], { stdio: 'pipe', timeout: 30_000 });
  if (result.status === 0) {
    log('Bun runtime', true, `v${result.stdout?.toString().trim()}`);
  } else {
    log('Bun runtime', false, 'Cannot run bun. Install: brew install oven-sh/bun/bun (macOS) or npm install -g bun');
  }
}

async function checkRunningChromeConflict(): Promise<void> {
  if (process.platform !== 'darwin') return;

  const result = spawnSync('pgrep', ['-f', 'Google Chrome'], { stdio: 'pipe' });
  const pids = result.stdout?.toString().trim().split('\n').filter(Boolean) || [];

  if (pids.length > 0) {
    warn('Running Chrome instances', `${pids.length} Chrome process(es) detected. The skill uses --user-data-dir for isolation, so this is safe. Paste keystroke targets Chrome by app name (minor risk if multiple Chrome windows visible).`);
  } else {
    log('Running Chrome instances', true, 'No existing Chrome processes');
  }
}

async function main(): Promise<void> {
  console.log('=== obsidian-to-x: Permission & Environment Check ===\n');

  await checkChrome();
  await checkProfileIsolation();
  await checkBun();
  await checkAccessibility();
  await checkClipboardCopy();
  await checkPasteKeystroke();
  await checkRunningChromeConflict();

  console.log('\n--- Summary ---');
  const failed = results.filter((r) => !r.ok);
  if (failed.length === 0) {
    console.log('All checks passed. Ready to post to X.');
  } else {
    console.log(`${failed.length} issue(s) found:`);
    for (const f of failed) {
      console.log(`  ❌ ${f.name}: ${f.detail}`);
    }
    process.exit(1);
  }
}

await main().catch((err) => {
  console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
  process.exit(1);
});
