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 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
                    })
                }

                // Also check for supported versions list (e.g. older 8.x)
                if (majorEntry.supported_versions) {
                    for (const branch of majorEntry.supported_versions) {
                        // This JSON doesn't give specific patch versions for these branches, 
                        // but winData usually covers the latest of each branch anyway.
                    }
                }
            }

            // 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
        }
    }
}

export const phpManagerService = new PhpManagerService()
