import fs from 'fs'
import path from 'path'
import axios from 'axios'
import { configService } from './ConfigService'
import { portMappingService } from './PortMappingService'

export interface PhpVersion {
    id: string
    version: string
    url: string
    status: 'available' | 'downloading' | 'installed'
    progress: number
    port?: number
}

export interface PhpExtension {
    name: string
    dll: string
    enabled: boolean
}

export class PhpManagerService {
    private versions: PhpVersion[] = []
    private binDir: string
    private winPhpUrl = 'https://windows.php.net/downloads/releases/'
    private winArchiveUrl = 'https://windows.php.net/downloads/releases/archives/'
    private winReleasesJsonUrl = 'https://windows.php.net/downloads/releases/releases.json'
    private mainReleasesJsonUrl = 'https://www.php.net/releases/index.php?json'

    constructor() {
        this.binDir = path.join(configService.getBasePath(), 'bin')
        this.init()
    }

    private async init() {
        if (!fs.existsSync(this.binDir)) {
            fs.mkdirSync(this.binDir, { recursive: true })
        }
        await this.fetchAvailableVersions()
    }

    private async fetchAvailableVersions() {
        try {
            // 1. Fetch from windows.php.net for current releases
            const winResponse = await axios.get(this.winReleasesJsonUrl, {
                headers: { 'User-Agent': 'Mozilla/5.0' }
            }).catch(() => ({ data: {} }))
            const winData = winResponse.data

            // 2. Fetch from main php.net for the comprehensive version list
            const mainResponse = await axios.get(this.mainReleasesJsonUrl, {
                headers: { 'User-Agent': 'Mozilla/5.0' }
            })
            const mainData = mainResponse.data

            const dynamicVersions: Map<string, PhpVersion> = new Map()

            // Strategy A: Direct matches from windows releases.json (current & stable)
            for (const branch in winData) {
                const release = winData[branch]
                const version = release.version
                // prioritize ts-vs16/17-x64
                const tsBuild = release['ts-vs16-x64'] || release['ts-vs17-x64'] || release['ts-vc15-x64'] || release['ts-vc14-x64']

                if (tsBuild && tsBuild.zip) {
                    dynamicVersions.set(version, {
                        id: `php-${version}`,
                        version: version,
                        url: this.winPhpUrl + tsBuild.zip.path,
                        status: 'available',
                        progress: 0
                    })
                }
            }

            // Strategy B: Legacy versions from main php.net JSON
            for (const majorKey in mainData) {
                const majorEntry = mainData[majorKey]
                const latestVersion = majorEntry.version

                if (!dynamicVersions.has(latestVersion)) {
                    // Construct archive URL for older versions like 5.6.40, 7.4.33 etc if not in winData
                    // Pattern: php-VERSION-Win32-COMPILER-ARCH.zip
                    let arch = 'x64'
                    let compiler = 'vc15'

                    if (latestVersion.startsWith('5.6')) compiler = 'vc11'
                    else if (latestVersion.startsWith('7.0') || latestVersion.startsWith('7.1')) compiler = 'vc14'
                    else if (latestVersion.startsWith('7.2') || latestVersion.startsWith('7.3') || latestVersion.startsWith('7.4')) compiler = 'vc15'
                    else compiler = 'vs16'

                    const url = `${this.winArchiveUrl}php-${latestVersion}-Win32-${compiler}-${arch}.zip`

                    // We add it, noting that older versions might have different naming conventions
                    // But for 5.6 and up, this pattern is generally consistent for x64 TS.
                    dynamicVersions.set(latestVersion, {
                        id: `php-${latestVersion}`,
                        version: latestVersion,
                        url: url,
                        status: 'available',
                        progress: 0
                    })
                }

            }

            // De-duplicate and sort
            this.versions = Array.from(dynamicVersions.values()).sort((a, b) => {
                // Semi-numeric sort to handle versions correctly
                const partsA = a.version.split('.').map(Number)
                const partsB = b.version.split('.').map(Number)
                for (let i = 0; i < 3; i++) {
                    if ((partsA[i] || 0) > (partsB[i] || 0)) return -1
                    if ((partsA[i] || 0) < (partsB[i] || 0)) return 1
                }
                return 0
            })

        } catch (e) {
            console.error('Failed to fetch PHP versions:', e)
        }
    }

    private refreshInstalledStatus() {
        this.versions = this.versions.map(v => {
            const targetDir = path.join(this.binDir, v.id)
            const phpExe = path.join(targetDir, 'php.exe')
            if (fs.existsSync(phpExe)) {
                return { ...v, status: 'installed', progress: 100 }
            }
            return v
        })
    }

    async getVersions(): Promise<PhpVersion[]> {
        if (this.versions.length === 0) {
            await this.fetchAvailableVersions()
        }
        this.refreshInstalledStatus()
        // Add ports for all versions
        return this.versions.map(v => ({
            ...v,
            port: portMappingService.getPhpPort(v.version)
        }))
    }

    updateProgress(id: string, progress: number, status: PhpVersion['status'] = 'downloading') {
        const version = this.versions.find(v => v.id === id)
        if (version) {
            version.progress = progress
            version.status = status
        }
    }

    async getPhpBinaryPath(version: string): Promise<string | null> {
        const targetDir = path.join(this.binDir, `php-${version}`)
        const phpExe = path.join(targetDir, 'php.exe')
        if (fs.existsSync(phpExe)) {
            return phpExe
        }
        return null
    }

    async getExtensions(version: string): Promise<PhpExtension[]> {
        const phpDir = path.join(this.binDir, `php-${version}`)
        const extDir = path.join(phpDir, 'ext')
        const iniPath = path.join(phpDir, 'php.ini')

        if (!fs.existsSync(phpDir)) return []

        // Ensure php.ini exists
        if (!fs.existsSync(iniPath)) {
            const devIni = path.join(phpDir, 'php.ini-development')
            const prodIni = path.join(phpDir, 'php.ini-production')
            if (fs.existsSync(devIni)) fs.copyFileSync(devIni, iniPath)
            else if (fs.existsSync(prodIni)) fs.copyFileSync(prodIni, iniPath)
        }

        const extensions: PhpExtension[] = []
        if (fs.existsSync(extDir)) {
            const files = fs.readdirSync(extDir)
            for (const file of files) {
                if (file.startsWith('php_') && file.endsWith('.dll')) {
                    const name = file.slice(4, -4)
                    extensions.push({ name, dll: file, enabled: false })
                }
            }
        }

        if (fs.existsSync(iniPath)) {
            const content = fs.readFileSync(iniPath, 'utf8')
            const lines = content.split(/\r?\n/)
            for (const ext of extensions) {
                // Check for extension=php_NAME.dll or extension=NAME (newer PHP)
                const pattern = new RegExp(`^\\s*extension\\s*=\\s*(?:php_)?${ext.name}(?:\\.dll)?\\s*$`, 'i')
                ext.enabled = lines.some(line => pattern.test(line.trim()))
            }
        }

        return extensions
    }

    async toggleExtension(version: string, extName: string, enabled: boolean): Promise<{ success: boolean; message: string }> {
        const phpDir = path.join(this.binDir, `php-${version}`)
        const iniPath = path.join(phpDir, 'php.ini')

        if (!fs.existsSync(iniPath)) return { success: false, message: 'php.ini not found' }

        let content = fs.readFileSync(iniPath, 'utf8')
        const lines = content.split(/\r?\n/)

        let found = false
        const newLines = lines.map(line => {
            const trimmedLine = line.trim()
            const pattern = new RegExp(`^\\s*;?\\s*extension\\s*=\\s*(?:php_)?${extName}(?:\\.dll)?\\s*$`, 'i')

            if (pattern.test(trimmedLine)) {
                found = true
                // Keep the same format as found if possible, but for enabled we enforce a clean one
                // For Windows, extension=php_name.dll is safest even in newer versions, 
                // but extension=name is cleaner. We'll use a slightly smarter approach:
                let extValue = extName
                if (version.startsWith('5.') || version.startsWith('7.')) {
                    extValue = `php_${extName}.dll`
                }
                return enabled ? `extension=${extValue}` : `;extension=${extValue}`
            }
            return line
        })

        if (!found && enabled) {
            let extValue = extName
            if (version.startsWith('5.') || version.startsWith('7.')) {
                extValue = `php_${extName}.dll`
            }
            const newLine = `extension=${extValue}`
            const extSectionIndex = newLines.findIndex(l => l.trim().toLowerCase() === '; dynamic extensions')
            if (extSectionIndex !== -1) {
                newLines.splice(extSectionIndex + 1, 0, newLine)
            } else {
                newLines.push(newLine)
            }
        }

        // Also ensure extension_dir = "ext"
        let extDirSet = false
        const finalLines = newLines.map(line => {
            if (line.trim().startsWith('extension_dir') || line.trim().startsWith(';extension_dir')) {
                extDirSet = true
                return 'extension_dir = "ext"'
            }
            return line
        })

        if (!extDirSet) {
            finalLines.unshift('extension_dir = "ext"')
        }

        fs.writeFileSync(iniPath, finalLines.join('\n'), 'utf8')
        return { success: true, message: `Extension ${extName} ${enabled ? 'enabled' : 'disabled'}` }
    }
}

export const phpManagerService = new PhpManagerService()
