<?php

//git archive --format=tar --remote=git@bitbucket.org:filoblu/elisabetta-franchi-2.0.git master -- composer.lock |tar xf -
//git archive --format=tar --remote=git@bitbucket.org:filoblu/elisabetta-franchi-2.0.git master -- 'deploy/*.patch' |tar xf -

interface ComposerLockResolverInterface
{

    /**
     * setUrl
     *
     * @param  mixed $url
     *
     * @return void
     */
    function setUrl($url);

    /**
     * canHandle
     *
     * @param  mixed $url
     *
     * @return void
     */
    function canHandle($url);

    /**
     * fetch
     *
     * @return void
     */
    function fetch();

    /**
     * getContent
     *
     * @return void
     */
    function getContent();

}

class FileComposerLockResolver implements ComposerLockResolverInterface
{
    protected $content;

    protected $path;

     /**
     * canHandle
     *
     * @param  mixed $url
     *
     * @return void
     */
    public function canHandle($url)
    {
        return is_file($url);
    }

    /**
     * setUrl
     *
     * @param  mixed $url
     *
     * @return void
     */
    public function setUrl($url)
    {
        $this->$url = $url;
        return $this;
    }

    /**
     * fetch
     *
     * @return ComposerLockResolverInterface
     */
    public function fetch()
    {
       $this->content = file_get_contents($this->url);
        return $this;
    }

    /**
     * getContent
     *
     * @return string
     */
    public function getContent()
    {
        return $this->content;
    }
}
class GitComposerLockResolver implements ComposerLockResolverInterface
{

    protected $content;

    protected $url;

    /**
     * canHandle
     *
     * @param  mixed $url
     *
     * @return void
     */
    public function canHandle($url)
    {
        return (false !== strpos($url, 'git@'));
    }

    /**
     * setUrl
     *
     * @param  mixed $url
     *
     * @return void
     */
    public function setUrl($url)
    {
        $this->url = $url;
        return $this;
    }

    /**
     * fetch
     *
     * @param  string $url
     *
     * @return ComposerLockResolverInterface
     */
    public function fetch()
    {
        ob_start();
        system("git archive --format=tar --remote={$this->url} master -- composer.lock | tar xOf -");
        $this->content = ob_get_contents();
        ob_end_clean();
        return $this;
    }

    /**
     * getContent
     *
     * @return string
     */
    public function getContent()
    {
        return $this->content;
    }
    
}

class ComposerLockExtractor
{
    /**
     *
     */
    protected $versionFiles = 'db.json';

    /**
     *
     */
    protected $versions;

    
    /**
     * __construct
     *
     * @return void
     */
    public function __construct()
    {
        $this->versions = [];

        if (file_exists($this->versionFiles)) {
            $this->versions = json_decode(file_get_contents($this->versionFiles), true);
        }
    }

    /**
     * getStoredVersion
     *
     * @param  mixed $patch
     * @param  mixed $version
     *
     * @return void
     */
    protected function getStoredVersion($patch, $version)
    {
        $v['max'] = $version;
        $v['min'] = $version;

        if ($this->versions === null) {
            return $v;
        }

        if (isset($this->versions[$patch])) {
            // Find minimum version
            switch (version_compare($version, $this->versions[$patch]['min'])) {
                case 0:
                    break;

                case -1:
                    $v['min'] = $version;
                    break;

                case 1:
                    $v['min'] = $this->versions[$patch]['min'];
                    break;
            }

            // Find max version

            switch (version_compare($version, $this->versions[$patch]['max'])) {
                case 0:
                    break;

                case -1:
                    $v['max'] = $this->versions[$patch]['max'];
                    break;

                case 1:
                    $v['max'] = $version;
                    break;
            }
        }

        return $v;
    }

    public function extract($json)
    {
        $composerLock = json_decode($json, true);
        $output = [];

        foreach ($composerLock['packages'] as $package) {

            if (!isset($package['extra']['patches_applied'])) {
                continue;
            }

            $partial = [];
            $name = $package['name'];
            $version = $package['version'];

            foreach ($package['extra']['patches_applied'] as $title => $patch) {
                $partial[] = [
                    "title" => $title,
                    "url" => $patch,
                ];

                $vv = $this->getStoredVersion($patch, $version);
              //  echo "$patch -> $name {$vv['min']} - {$vv['max']}\n";

                $this->versions[$patch] = [
                    "module" => $name,
                    "title" => $title,
                    "min" => $vv['min'],
                    "max" => $vv['max'],
                ];

                // $output[$name][$minVersion] = $partial;

            }
        }
        //   ksort($output);

        file_put_contents("db.json", json_encode($this->versions, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
        return $output;

    }

    public function getPatchCount()
    {
        return count($this->versions);
    }

    public function dump($dumper)
    {
        return $dumper->dump($this->versions);
    }
}


interface DumperInterface
{
    function dump($patches);
}

class VaimoComposerPatchesDumper implements DumperInterface
{
    
    /**
     * getVersionConstraint
     *
     * @param  mixed $min
     * @param  mixed $max
     *
     * @return string
     */
    protected function getVersionConstraint($min , $max)
    {
        if( $min == $max)
        {
            return $min;
        }

        return ">={$min} <={$max}";
    }
    
    /**
     * dump
     * "extra": {
     * " patches": {
     *    "phar-io/version": {
     *       "[BUGFIX 1] - applies when targeted/package version is less than 1.2.3)": {
     *           ">=1.0.0 <=2.4.0": "test.patch"
     *       },
     *       "[BUGFIX 2] - applies when targeted/package version is less than 1.2.3)": {
     *           ">=1.0.0 <=2.4.0": "test2.patch"
     *       }
     *   }
     * }
     * }
     * @param  array $patches
     *
     * @return string
     */
    public function dump($patches)
    {
        $output = [];
        foreach($patches as $patch => $info)
        {
            $output[$info['module']][$info['title']] = 
                     [
                         $this->getVersionConstraint($info['min'], $info['max']) => str_replace('deploy/','patches/' ,$patch)
                     ];
            
            
        }
        return json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
    }
}

//$data = $extractor->extract($argv[1]);

//echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$repos = [
    'git@bitbucket.org:filoblu/albertoguardiani.git',
    'git@bitbucket.org:filoblu/antony-morato-b2c.git',
    'git@bitbucket.org:filoblu/autre-chose.git',
    'git@bitbucket.org:filoblu/blondieshop.git',
    'git@bitbucket.org:filoblu/braintropycommerce.git',
    'git@bitbucket.org:filoblu/campomaggi.git',
    'git@bitbucket.org:filoblu/elisabetta-franchi-2.0.git',
    'git@bitbucket.org:filoblu/frau.git',
    'git@bitbucket.org:filoblu/guzzini.git',
    'git@bitbucket.org:filoblu/lamborghini-b2b.git',  
    'git@bitbucket.org:filoblu/lotto.git',
    'git@bitbucket.org:filoblu/manilagrace.git',
    'git@bitbucket.org:filoblu/paolo-scafora.git',
    'git@bitbucket.org:filoblu/peuterey.git',
    'git@bitbucket.org:filoblu/reef.git',
    'git@bitbucket.org:filoblu/tcx.git',
    'git@bitbucket.org:filoblu/testoni.git',
    'git@bitbucket.org:filoblu/zanellato.git'
];

/*
foreach($repos as $repo)
{
    system("git archive --format=tar --remote={$repo} master -- 'deploy/*.patch' |tar xf -");
}

$extractor = new ComposerLockExtractor();
$resolver = new GitComposerLockResolver();


foreach($repos as $repo)
{
    echo "Extracting from {$repo} ... ";
    $extractor->extract($resolver->setUrl($repo)->fetch()->getContent());
    echo "done\n";
}
*/
$extractor = new ComposerLockExtractor();

file_put_contents( 'patches.json' ,$extractor->dump(new VaimoComposerPatchesDumper()));

echo "Extracted " . $extractor->getPatchCount() . PHP_EOL;
