import { exec } from 'child_process'
import { promisify } from 'util'
import type { TrackedProcess, TrackingStats, TrackedGame } from '../../shared/types'
import type { StoreType } from '../storeTypes'
import { updateGameTime, getTrackedGames } from './timeStorage'
import { updateTrayMenu } from '../tray'

const execAsync = promisify(exec)

interface ProcessInfo {
  ProcessName: string
  MainWindowTitle: string
  Id: number
}

export async function getRunningProcesses(): Promise<TrackedProcess[]> {
  try {
    const { stdout } = await execAsync(
      'powershell -NoProfile -Command "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-Process | Where-Object {$_.MainWindowTitle} | Select-Object ProcessName, MainWindowTitle, Id | ConvertTo-Json -Compress"',
      { encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 }
    )

    if (!stdout.trim()) {
      return []
    }

    let processes: ProcessInfo[]
    try {
      const parsed = JSON.parse(stdout)
      processes = Array.isArray(parsed) ? parsed : [parsed]
    } catch {
      return []
    }

    return processes.map((proc) => ({
      id: proc.Id.toString(),
      name: proc.ProcessName,
      displayName: proc.MainWindowTitle || proc.ProcessName,
      windowTitle: proc.MainWindowTitle,
      isGame: isLikelyGame(proc.ProcessName, proc.MainWindowTitle),
    }))
  } catch (error) {
    console.error('Failed to get running processes:', error)
    return []
  }
}

export async function getForegroundWindow(): Promise<string | null> {
  try {
    // Use base64 encoded script to avoid escaping issues
    const script = `
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class FGWindow {
    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll")]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
}
"@
\$hwnd = [FGWindow]::GetForegroundWindow()
\$processId = 0
[void][FGWindow]::GetWindowThreadProcessId(\$hwnd, [ref]\$processId)
\$proc = Get-Process -Id \$processId -ErrorAction SilentlyContinue
if (\$proc) { Write-Output \$proc.ProcessName }
`
    // Encode script as base64
    const base64Script = Buffer.from(script, 'utf16le').toString('base64')

    const { stdout } = await execAsync(
      `powershell -NoProfile -ExecutionPolicy Bypass -EncodedCommand ${base64Script}`,
      { encoding: 'utf8', timeout: 5000 }
    )

    const result = stdout.trim()
    return result || null
  } catch (error) {
    console.error('[getForegroundWindow] Error:', error)
    return null
  }
}

function isLikelyGame(processName: string, windowTitle: string): boolean {
  const gameIndicators = [
    'game', 'steam', 'epic', 'uplay', 'origin', 'battle.net',
    'unity', 'unreal', 'godot', 'ue4', 'ue5',
  ]

  const lowerName = processName.toLowerCase()
  const lowerTitle = (windowTitle || '').toLowerCase()

  // Check for common game launchers/engines
  for (const indicator of gameIndicators) {
    if (lowerName.includes(indicator) || lowerTitle.includes(indicator)) {
      return true
    }
  }

  // Exclude common non-game processes
  const nonGameProcesses = [
    'explorer', 'chrome', 'firefox', 'edge', 'opera', 'brave',
    'code', 'idea', 'webstorm', 'pycharm', 'rider',
    'discord', 'slack', 'teams', 'zoom', 'telegram',
    'spotify', 'vlc', 'foobar', 'winamp',
    'notepad', 'word', 'excel', 'powerpoint', 'outlook',
    'cmd', 'powershell', 'terminal', 'windowsterminal',
  ]

  for (const nonGame of nonGameProcesses) {
    if (lowerName.includes(nonGame)) {
      return false
    }
  }

  return true
}

interface GameEvent {
  gameName: string
  gameId: string
  duration?: number
}

export class ProcessTracker {
  private intervalId: NodeJS.Timeout | null = null
  private currentGame: string | null = null
  private currentGameName: string | null = null
  private sessionStart: number | null = null
  private store: StoreType
  private onUpdate: (stats: TrackingStats) => void
  private onGameStarted: (event: GameEvent) => void
  private onGameStopped: (event: GameEvent) => void

  constructor(
    store: StoreType,
    onUpdate: (stats: TrackingStats) => void,
    onGameStarted?: (event: GameEvent) => void,
    onGameStopped?: (event: GameEvent) => void
  ) {
    this.store = store
    this.onUpdate = onUpdate
    this.onGameStarted = onGameStarted || (() => {})
    this.onGameStopped = onGameStopped || (() => {})
  }

  start() {
    const settings = this.store.get('settings')
    const interval = settings.trackingInterval || 5000

    this.intervalId = setInterval(() => this.tick(), interval)
    console.log(`Process tracker started with ${interval}ms interval`)
  }

  stop() {
    if (this.intervalId) {
      clearInterval(this.intervalId)
      this.intervalId = null
    }

    // End current session if any
    if (this.currentGame && this.sessionStart) {
      const duration = Date.now() - this.sessionStart
      updateGameTime(this.store, this.currentGame, duration)
    }

    this.currentGame = null
    this.sessionStart = null
    console.log('Process tracker stopped')
  }

  private async tick() {
    const foregroundProcess = await getForegroundWindow()
    const trackedGames = getTrackedGames(this.store)

    // Debug logging - ALWAYS log
    console.log('[Tracker] Foreground:', foregroundProcess || 'NULL', '| Tracked:', trackedGames.length, 'games:', trackedGames.map(g => g.executableName).join(', ') || 'none')

    // Find if foreground process matches any tracked game
    let matchedGame: TrackedGame | null = null
    if (foregroundProcess) {
      const lowerForeground = foregroundProcess.toLowerCase().replace('.exe', '')
      for (const game of trackedGames) {
        const lowerExe = game.executableName.toLowerCase().replace('.exe', '')
        // More flexible matching
        const matches = lowerForeground === lowerExe ||
                       lowerForeground.includes(lowerExe) ||
                       lowerExe.includes(lowerForeground)
        if (matches) {
          console.log('[Tracker] MATCH:', foregroundProcess, '===', game.executableName)
          matchedGame = game
          break
        }
      }
    }

    // Handle game state changes
    if (matchedGame && matchedGame.id !== this.currentGame) {
      // New game started
      if (this.currentGame && this.sessionStart && this.currentGameName) {
        // End previous session
        const duration = Date.now() - this.sessionStart
        updateGameTime(this.store, this.currentGame, duration)
        // Emit game stopped event for previous game
        this.onGameStopped({
          gameName: this.currentGameName,
          gameId: this.currentGame,
          duration
        })
      }

      this.currentGame = matchedGame.id
      this.currentGameName = matchedGame.name
      this.sessionStart = Date.now()
      console.log(`Started tracking: ${matchedGame.name}`)
      updateTrayMenu(null, true, matchedGame.name)
      // Emit game started event
      this.onGameStarted({
        gameName: matchedGame.name,
        gameId: matchedGame.id
      })
    } else if (!matchedGame && this.currentGame) {
      // Game stopped
      if (this.sessionStart) {
        const duration = Date.now() - this.sessionStart
        updateGameTime(this.store, this.currentGame, duration)
        // Emit game stopped event
        if (this.currentGameName) {
          this.onGameStopped({
            gameName: this.currentGameName,
            gameId: this.currentGame,
            duration
          })
        }
      }

      console.log(`Stopped tracking: ${this.currentGame}`)
      this.currentGame = null
      this.currentGameName = null
      this.sessionStart = null
      updateTrayMenu(null, false)
    }

    // Emit update
    const stats = this.getStats()
    this.onUpdate(stats)
  }

  private getStats(): TrackingStats {
    const trackedGames = getTrackedGames(this.store)
    const now = Date.now()
    const todayStart = new Date().setHours(0, 0, 0, 0)
    const weekStart = now - 7 * 24 * 60 * 60 * 1000
    const monthStart = now - 30 * 24 * 60 * 60 * 1000

    let totalTimeToday = 0
    let totalTimeWeek = 0
    let totalTimeMonth = 0

    // Add current session time if active
    if (this.currentGame && this.sessionStart) {
      const currentSessionTime = now - this.sessionStart
      totalTimeToday += currentSessionTime
      totalTimeWeek += currentSessionTime
      totalTimeMonth += currentSessionTime
    }

    // This is a simplified version - full implementation would track sessions with timestamps
    for (const game of trackedGames) {
      totalTimeMonth += game.totalTime
      // For simplicity, assume all recorded time is from this week/today
      // A full implementation would store session timestamps
    }

    return {
      totalTimeToday,
      totalTimeWeek,
      totalTimeMonth,
      sessions: [],
      currentGame: this.currentGameName,
      currentSessionDuration: this.currentGame && this.sessionStart ? now - this.sessionStart : 0,
    }
  }
}
