<?php

namespace PortalPeople\packagemanager\controllers;

use Yii;
use yii\web\Controller;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use yii\web\Response;
use yii\helpers\Json;
use Composer\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;

/**
 * PackageManagerController handles Composer package management
 */
class PackageManagerController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::class,
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@'], // Only authenticated users
                    ],
                ],
            ],
            'verbs' => [
                'class' => VerbFilter::class,
                'actions' => [
                    'delete' => ['POST'],
                    'install' => ['POST'],
                    'update' => ['POST'],
                ],
            ],
        ];
    }

    /**
     * List all packages from Satis repository
     */
    public function actionIndex()
    {
        $composerJson = $this->getComposerJson();
        $satisPackages = $this->fetchSatisPackages();
        $installedPackages = $composerJson['require'] ?? [];

        return $this->render('index', [
            'satisPackages' => $satisPackages,
            'installedPackages' => $installedPackages,
        ]);
    }

    /**
     * Install a package
     */
    public function actionInstall()
    {
        Yii::$app->response->format = Response::FORMAT_JSON;
        
        $packageName = Yii::$app->request->post('package');
        $version = Yii::$app->request->post('version', '*');

        if (!$packageName) {
            return ['success' => false, 'message' => 'Package name is required'];
        }

        try {
            // Add to composer.json
            $this->addPackageToComposer($packageName, $version);
            
            // Run composer install via shell
            $output = $this->runComposerCommand('update');
            
            return [
                'success' => true,
                'message' => "Package {$packageName} installed successfully",
                'output' => $output,
            ];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }

    /**
     * Update a package
     */
    public function actionUpdate()
    {
        Yii::$app->response->format = Response::FORMAT_JSON;
        
        $packageName = Yii::$app->request->post('package');

        if (!$packageName) {
            return ['success' => false, 'message' => 'Package name is required'];
        }

        try {
            $output = $this->runComposerCommand("update {$packageName}");
            
            return [
                'success' => true,
                'message' => "Package {$packageName} updated successfully",
                'output' => $output,
            ];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }

    /**
     * Remove a package
     */
    public function actionDelete()
    {
        Yii::$app->response->format = Response::FORMAT_JSON;
        
        $packageName = Yii::$app->request->post('package');

        if (!$packageName) {
            return ['success' => false, 'message' => 'Package name is required'];
        }

        try {
            $this->removePackageFromComposer($packageName);
            $output = $this->runComposerCommand('update');
            
            return [
                'success' => true,
                'message' => "Package {$packageName} removed successfully",
                'output' => $output,
            ];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }

    /**
     * Get package details from Satis
     */
    public function actionDetails($package)
    {
        Yii::$app->response->format = Response::FORMAT_JSON;
        
        $satisPackages = $this->fetchSatisPackages();
        
        if (isset($satisPackages[$package])) {
            return ['success' => true, 'package' => $satisPackages[$package]];
        }
        
        return ['success' => false, 'message' => 'Package not found'];
    }

    /**
     * Fetch packages from Satis repository
     */
    protected function fetchSatisPackages()
    {
        $satisUrl = 'https://scupuhfsfu.portalpeople.co.uk/packages.json';
        
        try {
            // Load auth credentials from auth.json
            $authFile = Yii::getAlias('@app/../auth.json');
            $username = 'composer';
            $password = 'pHt3S8AaGnho9zQkfRTW';
            
            if (file_exists($authFile)) {
                $authData = Json::decode(file_get_contents($authFile));
                if (isset($authData['http-basic']['scupuhfsfu.portalpeople.co.uk'])) {
                    $username = $authData['http-basic']['scupuhfsfu.portalpeople.co.uk']['username'];
                    $password = $authData['http-basic']['scupuhfsfu.portalpeople.co.uk']['password'];
                }
            }
            
            // Create context with Basic Auth
            $context = stream_context_create([
                'http' => [
                    'header' => 'Authorization: Basic ' . base64_encode("$username:$password")
                ]
            ]);
            
            $json = file_get_contents($satisUrl, false, $context);
            $data = Json::decode($json);
            
            $packages = [];
            
            // Satis v2 uses 'available-packages' and metadata-url instead of direct packages list
            if (isset($data['available-packages'])) {
                foreach ($data['available-packages'] as $packageName) {
                    try {
                        // Fetch individual package metadata
                        $packageUrl = 'https://scupuhfsfu.portalpeople.co.uk/p2/' . $packageName . '.json';
                        $packageJson = file_get_contents($packageUrl, false, $context);
                        $packageData = Json::decode($packageJson);
                        
                        if (isset($packageData['packages'][$packageName])) {
                            $versions = $packageData['packages'][$packageName];
                            $latestVersion = $this->getLatestVersion($versions);
                            $packages[$packageName] = $latestVersion;
                        }
                    } catch (\Exception $e) {
                        Yii::warning("Failed to fetch package {$packageName}: " . $e->getMessage());
                        continue;
                    }
                }
            } elseif (isset($data['packages'])) {
                // Fallback for Satis v1 format
                foreach ($data['packages'] as $packageName => $versions) {
                    $latestVersion = $this->getLatestVersion($versions);
                    $packages[$packageName] = $latestVersion;
                }
            }
            
            return $packages;
        } catch (\Exception $e) {
            Yii::error("Failed to fetch Satis packages: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Get latest stable version from package versions
     */
    protected function getLatestVersion($versions)
    {
        // Filter out dev versions
        $stableVersions = array_filter($versions, function($version) {
            return !isset($version['version_normalized']) || 
                   (strpos($version['version_normalized'], 'dev') === false &&
                    strpos($version['version'], 'dev-') === false);
        });
        
        // Sort versions by time (newest first)
        if (!empty($stableVersions)) {
            uasort($stableVersions, function($a, $b) {
                $timeA = isset($a['time']) ? strtotime($a['time']) : 0;
                $timeB = isset($b['time']) ? strtotime($b['time']) : 0;
                return $timeB - $timeA; // Descending order
            });
            return reset($stableVersions);
        }
        
        // If no stable versions, return the first one
        return reset($versions);
    }

    /**
     * Get composer.json content
     */
    protected function getComposerJson()
    {
        $composerPath = Yii::getAlias('@app/../composer.json');
        $content = file_get_contents($composerPath);
        return Json::decode($content);
    }

    /**
     * Save composer.json
     */
    protected function saveComposerJson($data)
    {
        $composerPath = Yii::getAlias('@app/../composer.json');
        $content = Json::encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        return file_put_contents($composerPath, $content);
    }

    /**
     * Add package to composer.json
     */
    protected function addPackageToComposer($packageName, $version)
    {
        $composer = $this->getComposerJson();
        $composer['require'][$packageName] = $version;
        $this->saveComposerJson($composer);
    }

    /**
     * Remove package from composer.json
     */
    protected function removePackageFromComposer($packageName)
    {
        $composer = $this->getComposerJson();
        if (isset($composer['require'][$packageName])) {
            unset($composer['require'][$packageName]);
            $this->saveComposerJson($composer);
        }
    }

    /**
     * Run composer command using Composer API
     */
    protected function runComposerCommand($command)
    {
        $composerDir = Yii::getAlias('@app/..');
        
        // Set memory limit for composer operations
        @ini_set('memory_limit', '512M');
        
        // Change to composer directory
        $originalDir = getcwd();
        chdir($composerDir);
        
        try {
            putenv('COMPOSER_HOME=' . $composerDir);
            
            // Create Composer application
            $application = new Application();
            $application->setAutoExit(false);
            
            // Parse command into arguments
            $parts = explode(' ', trim($command));
            $commandName = array_shift($parts);
            
            $inputArray = array_merge(['command' => $commandName], $parts);
            $inputArray['--no-interaction'] = true;
            $inputArray['--working-dir'] = $composerDir;
            
            $input = new ArrayInput($inputArray);
            $output = new BufferedOutput();
            
            // Run the command
            $exitCode = $application->run($input, $output);
            
            $result = $output->fetch();
            
            if ($exitCode !== 0) {
                throw new \Exception("Composer command failed (exit code {$exitCode}): " . substr($result, 0, 500));
            }
            
            return $result;
            
        } finally {
            // Always restore original directory
            chdir($originalDir);
        }
    }
}
