<?php

namespace Prozesse;

class Schritt
{
  private $ID;
  private $Sektion;
  private $Name;
  private $Optional;
  private $Verantwortlich;
  private $Von;
  private $Wann;
  private $Vornamen;
  private $Nachnamen;
  private $Notiz;
  private $Unzutreffend;
  private $VerantwortlicheName;
  private $Indikatoren;
  private $Helfer;
  
  public $Nachfolger = [];
  public $Vorgänger = [];
  
  private $parent;
  private $db;
  private $man;
  
  public function __construct($parent, $database, $manager, $data) {
    $this->parent = $parent;
    $this->db = $database;
    $this->man = $manager;
    
    $this->ID = $data["ID"];
    $this->Sektion = $data["Sektion"];
    $this->Name = $data["Name"];
    $this->Optional = $data["Optional"];
    $this->Verantwortlich = $data["Verantwortlich"];
    $this->Von = null;
    $this->Wann = is_null($data["Wann"]) ? null : date("d.m.Y", strtotime($data["Wann"]));
    $this->Vornamen = $data["Vornamen"];
    $this->Nachnamen = $data["Nachnamen"];
    $this->Notiz = $data["Notiz"];
    $this->Unzutreffend = $data["Unzutreffend"];
    $this->VerantwortlicheName = $data["VerantwortlicheName"];
    $this->Indikatoren = json_decode(""!=$data["Indikatoren"] ? $data["Indikatoren"] : "[]", true);
    $this->Helfer = json_decode(""!=$data["Helfer"] ? $data["Helfer"] : "[]", true);
  }
  
  public function ID() {return $this->ID;}
  public function Sektion() {return $this->Sektion;}
  public function IsVerantwortlich() {return 1==$this->Verantwortlich;}
  public function IsFertig() {return null!==$this->Wann;}
  public function IsAktiv() {
    foreach ($this->Vorgänger as $pre) {
      if (!$pre["Vorgänger"]->IsFertig()) {
        return false;
    } }
    return true;
  }
  
  private function getVorbedingungen() {
    $ret = "";
    foreach ($this->Vorgänger as $pre) {
      if ($ret!="") {
        $ret.= ", ";
      }
      $ret.= $pre["Vorgänger"]->Name;
    }
    return $ret;
  }
  
  private function getIndikatoren() {
    $ret = [];
    foreach ($this->Indikatoren as $indikator) {
      switch ($indikator["check"]) {
        case "PNRRANGE":
          $success = true;
          
          if (isset($indikator["max"]) && $this->parent->Pnr() > $indikator["max"]) {
            $success = false;
          }
          if (isset($indikator["max"]) && $this->parent->Pnr() > $indikator["max"]) {
            $success = false;
          }
          $ret[] = ["ERFOLG" => $success, "NAME" => $indikator["name"] ?? ""];
          break;
    } }
    return $ret;
  }
  
  public function DoHelfer($id) {
    $helfer = $this->Helfer[$id];
    
    switch ($helfer["action"]) {
      case "update":
        switch ($helfer["table"]) {
          case "Personal":
            $qry = "UPDATE Personal SET ".$helfer["field"]." = ? WHERE ID=".$this->parent->ID();
            $this->man->AddMessage($qry." - ".$this->man->input["helferValue"]);
            $helferInput = $this->man->input["helferInput"][0] ?? "";
            if ($stmt = $this->db->prepare($qry)) {
              $stmt->bind_param("s", $this->man->input["helferValue"]);
              if ($stmt->execute()) {
                
              }
              return 200;
            } else {
              $this->man->AddMessage("Mysql error: ".$this->db->error);
              return 500;
            }
        }
        break;
    }
    return 404;
  }

  private function getHelfer() {
    $ret = [];
    $id = 0;
    foreach ($this->Helfer as $helfer) {
      switch ($helfer["action"]) {
        case "update":
          $ret[] = ["ID" => $id, "NAME" => $helfer["name"]];
          break;
      }
      $id++;
    }
    return $ret;
  }
  
  private function isReversierbar() {
    foreach ($this->Nachfolger as $post) {
      if ($post["Nachfolger"]->IsFertig() && !$post["Nachfolger"]->IsUnzutreffend()) {
        return false;
    } }
    return true;
  }
  
  public function IsUnzutreffend() {return 1==$this->Optional && 1==$this->Unzutreffend;}
  private function isOptional() {return 1==$this->Optional;}
  
  public function Get() {
    return [
      "ID" => $this->ID,
      "STATUS" => "__CASE__:".($this->IsFertig() ? ($this->IsUnzutreffend() ? "UNZUTREFFEND" : "FERTIG") : ($this->IsAktiv() ? "AKTIV" : "INAKTIV")),
      "NAME" => $this->Name,
      "VON" => (is_null($this->Vornamen) ? "" : preg_replace("/(?<![ -])\p{Ll}+/u", ".", $this->Vornamen)).$this->Nachnamen,
      "WANN" => $this->Wann,
      "NOTIZ" => $this->Notiz,
      "REVERSIERBAR" => $this->isReversierbar(),
      "OPTIONAL" => $this->isOptional(),
      "TODO" => $this->IsVerantwortlich(),
      "VORBEDINGUNG" => $this->getVorbedingungen(),
      "INDIKATOREN" => $this->getIndikatoren(),
      "HELFER" => $this->getHelfer(),
    ];
  }
  
  public function CheckStep($check) {
    $Unzutreffend = $this->man->input["Unzutreffend"] ?? 0;
    if ($check && ($this->IsAktiv() || 1==$Unzutreffend)) {
      $qry = "INSERT INTO Prozesse_Fortschritte (Personal, Schritt, Wann, Von, Notiz, Unzutreffend) VALUES (?, ?, NOW(), ?, ?, ?)";
      if ($stmt = $this->db->prepare($qry)) {
        $Notiz = $this->man->input["Notiz"] ?? "";
        $UserID = $this->man->user->ID();
        $Personal = $this->parent->ID();
        $stmt->bind_param("iiisi", $Personal, $this->ID, $UserID, $Notiz, $Unzutreffend);
        $stmt->execute();
        if (1==$stmt->affected_rows) {
          $this->Von = $UserID;
          $this->Wann = date("d.m.Y");
          $this->Vornamen = $this->man->user->Vornamen();
          $this->Nachnamen = $this->man->user->Nachnamen();
          $this->Notiz = $Notiz;
          $this->Unzutreffend = $Unzutreffend;
          
          if (1==$Unzutreffend) {
            foreach ($this->Nachfolger as $post) {
              if (1==$post["Bedingung"]) {
                $post["Nachfolger"]->CheckStep(true);
        } } } }
      } else {
        $this->man->AddMessage("Mysql error: ".$this->db->error);
        return 500;
      }
    } elseif (!$check && $this->IsFertig() && $this->isReversierbar()) {
      $qry = "DELETE FROM Prozesse_Fortschritte WHERE Personal = ? AND Schritt = ?";
      if ($stmt = $this->db->prepare($qry)) {
        $Personal = $this->parent->ID();
        $stmt->bind_param("ii", $Personal, $this->ID);
        $stmt->execute();
        if (1==$stmt->affected_rows) {
          $this->Von = null;
          $this->Wann = null;
          $this->Vornamen = null;
          $this->Nachnamen = null;
          $this->Notiz = null;
          $this->Unzutreffend = null;
        }
      } else {
        $this->man->AddMessage("Mysql error: ".$this->db->error);
        return 500;
    } }
    return 200;
  }
}

class Person
{
  private $ID;
  private $Nachnamen;
  private $Vornamen;
  private $OFnr;
  private $Pnr;
  
  private $parent;
  private $db;
  private $man;
  
  private $sektionen = [];
  private $schritte = [];
  
  public function __construct($parent, $database, $manager, $data) {
    $this->parent = $parent;
    $this->db = $database;
    $this->man = $manager;
    
    $this->ID = $data["ID"];
    $this->Nachnamen = $data["Nachnamen"];
    $this->Vornamen = $data["Vornamen"];
    $this->OFnr = $data["OFnr"];
    $this->Pnr = $data["Pnr"];
    
    $this->loadSchritte();
    $this->loadHierarchie();
  }
  
  public function ID() {return $this->ID;}
  public function Pnr() {return $this->Pnr;}
  
  private function loadSchritte() {
    $qry = "SELECT zs.ID, zs.Sektion, zs.Name, zs.Optional, zs.Indikatoren, zs.Helfer, IF(pv.Personal IS NULL, 0, 1) Verantwortlich, zf.Wann, "
          ."p.Vornamen, p.Nachnamen, zf.Notiz, zf.Unzutreffend, sv.Name VerantwortlicheName "
          ."FROM Prozesse_Schritte zs "
          ."LEFT JOIN Prozesse_Fortschritte zf ON zf.Schritt=zs.ID AND zf.Personal=".$this->ID." "
          ."LEFT JOIN Personal p ON p.ID=zf.Von "
          ."LEFT JOIN Personal_Verwalter pv ON pv.Verwalter=zs.Verantwortliche AND pv.Personal=".$this->man->user->ID()." "
          ."LEFT JOIN Struktur_Verwalter sv ON sv.ID=zs.Verantwortliche "
          ."WHERE zs.Prozess = ".$this->parent->ID()." "
          ."ORDER BY zs.Sektion ASC ";
    if ($res = $this->db->query($qry)) {
      while ($schritt = $res->fetch_assoc()) {
        $this->schritte[$schritt["ID"]] = new Schritt($this, $this->db, $this->man, $schritt);
        $this->sektionen[$schritt["Sektion"]] = array();
      }
    } else {
      $this->man->AddMessage("Mysql error: ".$this->db->error);
    }
  }
  
  private function loadHierarchie() {
    $qry = "SELECT zh.Schritt, zh.Vorgänger, zh.Bedingung "
          ."FROM Prozesse_Hierarchie zh "
          ."LEFT JOIN Prozesse_Schritte zs ON zs.ID=zh.Schritt "
          ."WHERE zs.Prozess = ".$this->parent->ID();
    if ($res = $this->db->query($qry)) {
      while ($schritt = $res->fetch_assoc()) {
        if (array_key_exists($schritt["Schritt"], $this->schritte) && array_key_exists($schritt["Vorgänger"], $this->schritte)) {
          $this->schritte[$schritt["Schritt"]]->Vorgänger[] = array(
            "Vorgänger" => &$this->schritte[$schritt["Vorgänger"]],
            "Bedingung" => $schritt["Bedingung"],
          );
          $this->schritte[$schritt["Vorgänger"]]->Nachfolger[] = array(
            "Nachfolger" => &$this->schritte[$schritt["Schritt"]],
            "Bedingung" => $schritt["Bedingung"],
          );
        }
      }
    } else {
      $this->man->AddMessage("Mysql error: ".$this->db->error);
    }
    
    $selectedIDs = array();
    for ($i = 0; $i<count($this->schritte); $i++) {
      foreach ($this->schritte as $ID => $values) {
        if (!in_array($ID, $selectedIDs)) {
          //Not selected yet. Check if pre-conditions have been selected
          $pass = true;
          foreach ($this->schritte[$ID]->Vorgänger as $pre) {
            if (!in_array($pre["Vorgänger"]->ID(), $selectedIDs)) {
              $pass = false;
          } }
          
          if ($pass) {
            $this->sektionen[$values->Sektion()][] = &$this->schritte[$ID];
            $selectedIDs[] = $ID;
            break;
  } } } } }
  
  private function isVollständig() {
    foreach ($this->schritte as $schritt) {
      if (!$schritt->IsFertig()) {
        return false;
    } }
    return true;
  }
  
  private function canFehlschlagen() {
    return (sizeof($this->parent->Fehlschlag())>0);
  }
  
  public function hasTodo() {
    foreach ($this->schritte as $schritt) {
      if ($schritt->IsVerantwortlich() && $schritt->IsAktiv()) {
        return true;
    } }
    return false;
  }
  
  private function getFortschritt() {
    $gesamt = 0;
    $fertig = 0;
    foreach ($this->schritte as $schritt) {
      $gesamt++;
      if ($schritt->IsFertig()) {
        $fertig++;
    } }
    return $fertig."/".$gesamt;
  }
  
  public function Get() {
    $ret = [
      "ID" => $this->parent->ID()."_".$this->ID,
      "MAIN" => [
        "NACHNAMEN" => $this->Nachnamen,
        "VORNAMEN" => $this->Vornamen,
        "FORTSCHRITT" => $this->getFortschritt(),
        "TODO" => $this->hasTodo(),
      ],
      "SUB" => [
        "SEKTION" => [],
        "ERFOLG" => $this->isVollständig(),
        "FEHLSCHLAG" => $this->canFehlschlagen()
      ]
    ];
    
    foreach ($this->sektionen as $sektion => $sekschritte) {
      $ret_sektion = ["NAME" => $sektion, "SCHRITT" => []];
      foreach ($sekschritte as $schritt) {
        $ret_sektion["SCHRITT"][] = $schritt->Get();
      }
      $ret["SUB"]["SEKTION"][] = $ret_sektion;
    }
    return $ret;
  }
  
  public function Finish($mode) {
    $liste = [];
    $protokoll = [];
    switch ($mode) {
      case "Success":
        if (!$this->isVollständig()) {
          $this->man->AddMessage("Der Prozess ist noch nicht vollständig abgeschlossen!");
          return 403;
        }
        $liste = $this->parent->Erfolg();
        $protokoll[] = "Prozess ".$this->parent->Name()." erfolgreich für ".$this->Vornamen." ".$this->Nachnamen;
        break;
      case "Fail":
        $liste = $this->parent->Fehlschlag();
        $protokoll[] = "Prozess ".$this->parent->Name()." abgebrochen für ".$this->Vornamen." ".$this->Nachnamen;
        break;
      default:
        return 400;
    }
    if (sizeof($liste)==0) {
      $this->man->AddMessage("Keine Aktionen definiert. Dieser Prozess lässt sich so nicht beenden!");
      return 400;
    }
    $protokoll[] = "";
    $protokoll[] = "<h3>Ausgeführte Aktionen:</h3>";
    foreach ($liste as $aktion) {
      switch ($aktion["action"]) {
        case "group_add":
          $this->db->query("INSERT INTO Personal_Gruppen (Personal, Gruppen) VALUES (".$this->ID.", ".$aktion["group"].")");
          $this->man->AddMessage("Die Person wurde einer neuen Gruppe zugeordnet. Ist sie dadurch in einem neuen Prozess, "
                                ."wird dieser erst bei Aktualisieren der Seite angezeigt");
          $protokoll[] = "Personal wurde Gruppe mit ID ".$aktion["group"]." zugeordnet.";
          break;
        case "group_remove":
          $this->db->query("DELETE FROM Personal_Gruppen WHERE Personal=".$this->ID." AND Gruppen=".$aktion["group"]);
          $protokoll[] = "Personal wurde aus Gruppe mit ID ".$aktion["group"]." entfernt.";
          break;
        case "user_remove":
          $this->db->query("DELETE FROM Personal WHERE ID=".$this->ID);
          $this->man->AddMessage("Die Person wurde vollständig aus dem System gelöscht!");
          $protokoll[] = "Personal wurde vollständig aus System gelöscht.";
          break;
        case "user_chgcategory":
        	$this->db->query("UPDATE Personal SET Kategorie='".$aktion["category"]."' WHERE ID=".$this->ID);
          $this->man->AddMessage("Personal wurde in andere Kategroie verschoben!");
          $protokoll[] = "Personal wurde vollständig aus System gelöscht.";
          break;
      }
    }
    $protokoll[] = "";
    $protokoll[] = "<h3>Protokoll der Prozesschritte:</h3>";
    foreach ($this->sektionen as $sektion => $sekschritte) {
      $protokoll[] = "<u>Sektion: ".$sektion."</u>";
      foreach ($sekschritte as $schritt) {
        $data = $schritt->Get();
        $status = explode(":", $data["STATUS"])[1];
        $protokoll[] = $data["NAME"].": "
                      .($status=="AKTIV" || $status=="INAKTIV"
                        ? "<span color='red'>Nicht abgeschlossen</span>" 
                        : "<span color='green'>".$status."</span>, Von: ".$data["VON"].", Wann: ".$data["WANN"].", Notiz: ".$data["NOTIZ"]);
    } }
    
    emlSendEmail("nils.otterpohl@feuerwehr-bs.net", $protokoll[0], implode("<br/>", $protokoll));
    emlSendEmail("verwaltung@fw-innenstadt.de", $protokoll[0], implode("<br/>", $protokoll));
    return 200;
  }
  
  public function CheckStep($schritt, $check) {
    return (isset($this->schritte[$schritt]) ? $this->schritte[$schritt]->CheckStep($check) : 404);
  }
  
  public function DoHelfer($schritt, $helfer) {
    return (isset($this->schritte[$schritt]) ? $this->schritte[$schritt]->DoHelfer($helfer) : 404);
  }
}

class Prozess
{
  private $parent;
  private $db;
  private $man;
  private $personen = [];
  
  private $ID;
  private $Name;
  private $Erfolg;
  private $Fehlschlag;
  
  public function __construct($parent, $database, $manager, $data, $id) {
    $this->parent = $parent;
    $this->db = $database;
    $this->man = $manager;
    
    $this->ID = $data["ID"];
    $this->Name = $data["Name"];
    $this->Erfolg = json_decode(""!=$data["Erfolg"] ? $data["Erfolg"] : "[]", true);
    $this->Fehlschlag = json_decode(""!=$data["Fehlschlag"] ? $data["Fehlschlag"] : "[]", true);
    
    $qry = "SELECT p.ID, p.OFnr, p.Pnr, p.Nachnamen, p.Vornamen "
          ."FROM Personal p "
          .$this->getWhereFromTarget($id, $data["Ziel"])
          ."ORDER BY Nachnamen ASC, Vornamen ASC ";
    if ($res = $this->db->query($qry)) {
      $i = 0;
      while ($person = $res->fetch_assoc()) {
        $this->personen[$person["ID"]] = new Person($this, $database, $manager, $person);
      }
    } else {
      echo $this->db->error;
    }
  }
  
  public function ID() {return $this->ID;}
  public function Name() {return $this->Name;}
  public function Erfolg() {return $this->Erfolg;}
  public function Fehlschlag() {return $this->Fehlschlag;}
  
  private function getWhereFromTarget($id, $target) {
    $joins = [];
    $wheres = [];
    $ret = "";
    
    if (null!==$id) {
      $wheres[] = "p.ID=".$id;
    }
    
    $json = json_decode($target, true);
    foreach ($json as $val) {
      switch ($val["condition"]) {
        case "group_member":
          $joins[] = "LEFT JOIN Personal_Gruppen pg ON pg.Personal=p.ID";
          $wheres[] = "pg.Gruppen=".$val["group"];
          break;
        case "cat_member":
          $wheres[] = "p.Kategorie=".$val["category"];
          break;
    } }
    
    foreach ($joins as $join) {$ret.= $join." ";}
    foreach ($wheres as $num => $where) {$ret.= ($num==0 ? "WHERE " : "AND ").$where." ";}
    return $ret;
  }
  
  public function Get() {
    $ret = [];
    foreach ($this->personen as $person) {
      $entry = $person->Get();
      $entry["MAIN"]["PROZESS"] = $this->Name;
      $entry["GROUP"] = $this->Name;
      $ret[] = $entry;
    }
    return $ret;
  }
  
  public function Finish($id, $mode) {
    if (isset($this->personen[$id])) {
      return $this->personen[$id]->Finish($mode);
    }
    $this->man->AddMessage("Personal nicht gefunden!");
    return 404;
  }
  
  public function CheckStep($schritt, $check) {
    $key = array_key_first($this->personen);
    return $this->personen[$key]->CheckStep($schritt, $check);
  }
  
  public function DoHelfer($schritt, $helfer) {
    $key = array_key_first($this->personen);
    return $this->personen[$key]->DoHelfer($schritt, $helfer);
  }
}

class Liste
{
  private $db;
  private $man;
  private $prozesse = [];
  
  public function __construct($database, $manager, $id = null) {
    $this->db = $database;
    $this->man = $manager;
    
    $split_id = isset($id) ? explode("_", $id) : [null, null];
    
    $qry = "SELECT ID, Name, Ziel, Erfolg, Fehlschlag FROM Prozesse".(is_null($split_id[0]) ? "" : " WHERE ID=".$split_id[0]);
    if ($res = $this->db->query($qry)) {
      while ($prozess = $res->fetch_assoc()) {
        $this->prozesse[$prozess["ID"]] = new Prozess($this, $database, $manager, $prozess, $split_id[1] ?? null);
      }
    }
  }
  
  public function Get() {
    $ret = [];
    foreach ($this->prozesse as $ID => $prozess) {
      $ret[] = ["ID" => $prozess->Name(), "ENTRIES" => $prozess->Get()];
      //$ret = array_merge($ret, $prozess->Get());
    }
    return (null!==$this->man->Main() && sizeof($ret)>0 ? $ret[0]["ENTRIES"][0] : $ret);
  }
  
  public function Finish($id, $mode) {
    $split_id = isset($id) ? explode("_", $id) : [null, null];
    if (isset($split_id[0], $split_id[1], $this->prozesse[$split_id[0]])) {
      return $this->prozesse[$split_id[0]]->Finish($split_id[1], $mode);
    }
    $this->man->AddMessage("Prozess nicht gefunden! ".$id." ".$this->man->ArrayToString($split_id));
    return (isset($split_id[0]) ? 404 : 400);
  }
  
  public function CheckStep($schritt, $check) {
    $key = array_key_first($this->prozesse);
    return $this->prozesse[$key]->CheckStep($schritt, $check);
  }
  
  public function DoHelfer($schritt, $helfer) {
    $key = array_key_first($this->prozesse);
    return $this->prozesse[$key]->DoHelfer($schritt, $helfer);
  }
}
