<?php
namespace nur\tools\compctl;

use nur\A;
use nur\cli\Application;
use nur\msg;
use nur\shutils;

class CompdepApp extends Application {
  private static function split_pkey_value(string $pv): array {
    if (preg_match('/([a-zA-Z0-9._-]+)=(.*)/', $pv, $ms)) {
      return [$ms[1], $ms[2]];
    } else {
      return [$pv, true];
    }
  }

  const SHOW_NAMES = "names", SHOW_DEPS = "deps", SHOW_DIRS = "dirs", SHOW_COMPOSER = "composer";
  const ARGS = [
    "purpose" => "Lister les dépendances d'un projet PHP",

    ["-B", "--projects-dir", "args" => "dir",
      "help" => "Spécifier le répertoire de base des projets",
    ],
    ["-w", "--rewrite",
      "help" => "modifier le fichier composer.json au lieu d'afficher le nouveau contenu",
    ],
    ["--force-rewrite",
      "help" => "en cas de modification, forcer la ré-écriture du fichier composer.json même s'il est inchangé",
    ],
    ["-s", "--set-pkey", "args" => 1, "argsdesc" => "PKEY=VALUE",
      "help" => "créer ou modifier le chemin de clé avec la valeur spécifiée",
    ],
    ["-d", "--del-pkey", "args" => 1, "argsdesc" => "PKEY",
      "help" => "supprimer le chemin de clé spécifié",
    ],
    ["-r", "--filter-dep", "args" => 1, "argsdesc" => "DEP",
      "help" => "sélectionner tous les projets qui ont une dépendance sur la librairie spécifiée",
    ],
    ["-u", "--update-dep", "args" => "value", "argsdesc" => "VERSION",
      "help" => "mettre à jour la dépendance avec la nouvelle cible/version.
requière l'utilisation de --filter-dep",
    ],
    ["--sn", "--show-names", "name" => "show", "value" => self::SHOW_NAMES,
      "help" => "afficher les noms des projets",
    ],
    ["--sp", "--show-deps", "name" => "show", "value" => self::SHOW_DEPS,
      "help" => "afficher les branches",
    ],
    ["--sb", "--show-branches",
      "help" => "avec --show-deps, afficher aussi les branches",
    ],
    ["--sf", "--show-filtered",
      "help" => "avec --show-deps, n'afficher que la dépendances sélectionnée
implique --show-branches",
    ],
    ["--sd", "--show-dirs", "name" => "show", "value" => self::SHOW_DIRS,
      "help" => "afficher les répertoires des projets",
    ],
    ["--sc", "--show-composer", "name" => "show", "value" => self::SHOW_COMPOSER,
      "help" => "afficher les chemins des fichiers composer.json de chaque projet",
    ],
  ];

  private $projectsDir;
  private $rewrite = false, $forceRewrite = false;
  private $setPkey, $delPkey;
  private $filterDep, $updateDep;
  private $show = self::SHOW_DEPS, $showBranches, $showFiltered;
  private $args;

  function main() {
    $projectDirs = $this->args;
    $rewrite = $this->rewrite;
    $forceRewrite = $this->forceRewrite;
    $setPkey = $this->setPkey;
    $delPkey = $this->delPkey;
    $filterDep = $this->filterDep;
    $updateDep = $this->updateDep;
    $projectsDir = $this->projectsDir;
    $show = $this->show;
    $showBranches = $this->showBranches;
    $showFiltered = $this->showFiltered;

    if ($updateDep && !$filterDep) {
      self::die("--update-dep requière --filter-dep");
    }

    if (!$projectDirs) {
      if ($projectsDir !== null) {
        $projectDirs = shutils::ls_pdirs($projectsDir);
      } else {
        if (file_exists("composer.json")) {
          $projectDirs = ["."];
        } else {
          $projectsDir = ".";
          $projectDirs = shutils::ls_pdirs($projectsDir);
        }
      }
    }
    $projects = ComposerWalker::build_projects($projectDirs, $projectsDir);
    $oprojects = ComposerWalker::order_deps(null, $projects);

    foreach ($oprojects as $name) {
      $project = A::get($projects, $name);
      if ($project === null) continue;
      $requires = ComposerWalker::order_deps($project["requires"], $projects);
      $branches = $project["branches"];
      if ($filterDep && !in_array($filterDep, $requires)) {
        continue;
      }

      if ($setPkey || $delPkey || $updateDep) {
        /** @var ComposerFile $composer */
        $composer = $project["composer"];
        msg::step($composer->getComposerFile());
        if ($setPkey) {
          [$pkey, $value] = self::split_pkey_value($setPkey);
          $composer->setPkey($pkey, $value, $rewrite, $forceRewrite);
        } elseif ($delPkey) {
          [$pkey, $value] = self::split_pkey_value($delPkey);
          $composer->delPkey($pkey, $rewrite, $forceRewrite);
        } elseif ($updateDep) {
          $composer->updateDep($filterDep, $updateDep, $rewrite, $forceRewrite);
        }
      } else {
        switch ($show) {
        case self::SHOW_NAMES:
          echo $name;
          break;
        case self::SHOW_DEPS:
          echo $name; $pos = strlen($name);
          $sep = ": ";
          foreach ($requires as $require) {
            if ($showFiltered && $require !== $filterDep) continue;
            $text = $require;
            if ($showBranches || $showFiltered) {
              $branch = A::get($branches, $require);
              if ($branch !== null) $text .= "[$branch]";
            }
            $size = strlen("$sep$text");
            if ($pos + $size > 80) {
              $sep = "\n".str_repeat(" ", 4); $pos = 4;
              $size = strlen($text);
            }
            echo "$sep$text"; $pos += $size;
            $sep = ", ";
          }
          break;
        case self::SHOW_DIRS:
          echo $project["dir"];
          break;
        case self::SHOW_COMPOSER:
          echo "$project[dir]/composer.json";
          break;
        }
        echo "\n";
      }
    }
  }
}
