import { spawn, ChildProcess } from 'child_process'
import path from 'path'
import { app } from 'electron'
import fs from 'fs'
import { configService } from './ConfigService'

export class ProcessManager {
    private processes: Map<string, ChildProcess> = new Map()
    private logs: Map<string, string[]> = new Map()
    private logDir: string

    constructor() {
        this.processes = new Map()
        this.logDir = ''
    }

    private initLogs() {
        if (this.logDir) return
        try {
            this.logDir = path.join(configService.getBasePath(), 'logs')
            if (!fs.existsSync(this.logDir)) {
                fs.mkdirSync(this.logDir, { recursive: true })
            }
            // Initialize with default logs
            ['nginx', 'php', 'mysql'].forEach(s => {
                if (!this.logs.has(s)) {
                    this.logs.set(s, [`[${new Date().toLocaleTimeString()}] Sistem hazır. İşlem bekleniyor...`])
                }
            })
        } catch (e) {
            console.error('Failed to init logs:', e)
        }
    }

    private addLog(name: string, data: string) {
        this.initLogs()
        if (!this.logs.has(name)) this.logs.set(name, [])
        const serviceLogs = this.logs.get(name)!
        const lines = data.split(/\r?\n/).filter(l => l.trim())
        const timestamp = new Date().toLocaleTimeString()

        lines.forEach(line => {
            const formattedLine = `[${timestamp}] ${line}`
            console.log(`[LOG:${name}] ${formattedLine}`)
            serviceLogs.push(formattedLine)
            // Persist to file
            if (this.logDir) {
                try {
                    fs.appendFileSync(path.join(this.logDir, `${name}.log`), formattedLine + '\n')
                } catch (e) {
                    // Ignore write errors to file if it happens
                }
            }
        })

        // Keep last 100 lines in memory
        if (serviceLogs.length > 100) {
            this.logs.set(name, serviceLogs.slice(-100))
        }
    }

    getLogs(name: string): string[] {
        this.initLogs()
        return this.logs.get(name) || []
    }

    async runOnce(binPath: string, args: string[], name?: string): Promise<boolean> {
        return new Promise((resolve) => {
            const child = spawn(binPath, args, { stdio: 'pipe', shell: false, windowsHide: true })

            child.stdout?.on('data', (data) => {
                const out = data.toString()
                console.log(`[INIT:${name || '?'}] ${out}`)
                if (name) this.addLog(name, out)
            })

            child.on('error', (err) => {
                console.error(`[INIT ERROR:${name || '?'}] ${err.message}`)
                if (name) this.addLog(name, `RunOnce Error: ${err.message}`)
                resolve(false)
            })

            child.stderr?.on('data', (data) => {
                const err = data.toString()
                console.error(`[INIT ERROR:${name || '?'}] ${err}`)
                if (name) this.addLog(name, err)
            })

            child.on('close', (code) => {
                resolve(code === 0)
            })
            child.on('error', (err) => {
                console.error('RunOnce error:', err)
                if (name) this.addLog(name, `RunOnce Error: ${err.message}`)
                resolve(false)
            })
        })
    }

    async startService(name: string, binPath: string, args: string[], shell: boolean = true): Promise<boolean> {
        if (this.processes.has(name)) {
            return true
        }

        if (!fs.existsSync(binPath)) {
            this.addLog(name, `Executable not found: ${binPath}`)
            return false
        }

        try {
            this.addLog(name, `Starting service with: ${binPath} ${args.join(' ')}`)

            const child = spawn(binPath, args, {
                shell: shell,
                windowsHide: true,
                stdio: ['ignore', 'pipe', 'pipe'],
                cwd: path.dirname(binPath)
            })

            console.log(`[PROCESS] Started ${name} with PID ${child.pid}`)
            this.addLog(name, `Process started (PID: ${child.pid})`)

            child.stdout?.on('data', (data) => this.addLog(name, data.toString()))
            child.stderr?.on('data', (data) => this.addLog(name, data.toString()))

            child.on('error', (err) => {
                this.addLog(name, `Failed to start: ${err.message}`)
                this.processes.delete(name)
            })

            child.on('exit', (code) => {
                this.addLog(name, `Exited with code ${code}`)
                if (code !== 0) {
                    this.checkErrorLogs(name)
                }
                this.processes.delete(name)
            })

            this.processes.set(name, child)

            return true
        } catch (error: any) {
            this.addLog(name, `Spawn Error: ${error.message}`)
            return false
        }
    }

    async stopService(name: string): Promise<boolean> {
        const child = this.processes.get(name)
        if (child) {
            this.addLog(name, 'Stopping service...')
            child.kill()
            this.processes.delete(name)
        }

        // Also force kill lingering processes
        return this.forceKillAll(name)
    }

    async forceKillAll(name: string): Promise<boolean> {
        return new Promise((resolve) => {
            const childProcess = this.processes.get(name)
            if (childProcess && childProcess.pid) {
                this.addLog(name, `Killing process by PID: ${childProcess.pid}`)
                const kill = spawn('taskkill', ['/F', '/PID', childProcess.pid.toString(), '/T'], {
                    windowsHide: true,
                    shell: false
                })
                kill.on('error', (err) => {
                    this.addLog(name, `PID Kill error: ${err.message}`)
                    this.processes.delete(name)
                    resolve(true)
                })
                kill.on('close', () => {
                    this.processes.delete(name)
                    resolve(true)
                })
                return
            }

            // Fallback: If it's a generic service name without version, or we just want to be sure
            // For MySQL multi-instance, we should avoid global kill if possible.
            const isVersionedMysql = name.startsWith('mysql:')
            const executableName = name === 'nginx' ? 'nginx.exe' : name === 'php' ? 'php-cgi.exe' : 'mysqld.exe'

            if (isVersionedMysql) {
                // For versioned mysql, we try to find by command line (contains data directory name)
                const mysqlId = name.replace('mysql:', 'mysql-')
                this.addLog(name, `Attempting to kill specific MySQL instance: ${mysqlId}`)
                // Using powershell to find and kill as wmic is deprecated/missing on some Win11
                const psCommand = `Get-CimInstance Win32_Process -Filter "name = 'mysqld.exe' AND CommandLine LIKE '%mysql_data_${mysqlId}%'" | Invoke-CimMethod -MethodName Terminate`
                const ps = spawn('powershell', ['-Command', psCommand], {
                    windowsHide: true,
                    shell: false
                })
                ps.on('error', (err) => {
                    this.addLog(name, `PowerShell kill error: ${err.message}`)
                    resolve(true)
                })
                ps.on('close', () => resolve(true))
            } else {
                this.addLog(name, `Force killing all ${executableName} processes...`)
                const tk = spawn('taskkill', ['/F', '/IM', executableName, '/T'], {
                    windowsHide: true,
                    shell: false
                })
                tk.on('error', (err) => {
                    this.addLog(name, `Taskkill error: ${err.message}`)
                    resolve(true)
                })
                tk.on('close', () => resolve(true))
            }
        })
    }

    isServiceRunning(name: string): boolean {
        return this.processes.has(name)
    }

    getStatuses(): Record<string, string> {
        const statuses: Record<string, string> = {}
        for (const name of Array.from(this.processes.keys())) {
            statuses[name] = 'running'
        }
        // Ensure defaults are present if stopped
        if (!statuses['nginx']) statuses['nginx'] = 'stopped'
        if (!statuses['mysql']) statuses['mysql'] = 'stopped'
        return statuses
    }

    private async checkErrorLogs(name: string) {
        if (!this.logDir) return
        const logPaths: Record<string, string> = {
            nginx: path.join(this.logDir, 'nginx_error.log'),
            mysql: path.join(this.logDir, 'mysql_error.log')
        }

        const logPath = logPaths[name]
        if (logPath && fs.existsSync(logPath)) {
            try {
                const content = fs.readFileSync(logPath, 'utf8')
                const lines = content.split('\n').slice(-10) // Get last 10 lines
                this.addLog(name, '--- ERROR LOG CONTENT ---')
                lines.forEach(l => {
                    if (l.trim()) this.addLog(name, `[CORE ERROR] ${l}`)
                })
            } catch (e) {
                console.error('Failed to read error log:', e)
            }
        }
    }

    stopAll(): void {
        for (const name of this.processes.keys()) {
            this.stopService(name)
        }
    }
}

export const processManager = new ProcessManager()
app.on('before-quit', () => processManager.stopAll())
