<?php

use FusionDirectory\Rest\WebServiceCall;

class Archive implements EndpointInterface
{
  private TaskGateway $gateway;

  public function __construct (TaskGateway $gateway)
  {
      $this->gateway = $gateway;
  }

    /**
     * @return array
     * Part of the interface of orchestrator plugin to treat GET method
     */
  public function processEndPointGet (): array
  {
      // Retrieve tasks of type 'archive'
      return $this->gateway->getObjectTypeTask('archive');
  }

    /**
     * @param array|null $data
     * @return array
     * @throws Exception
     * Note: Part of the interface of orchestrator plugin to treat PATCH method
     */
  public function processEndPointPatch (array $data = NULL): array
  {
      $result = [];
      $archiveTasks = $this->gateway->getObjectTypeTask('archive');

      // Initialize the WebServiceCall object for login
      $webServiceCall = new WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST');
      $webServiceCall->setCurlSettings(); // Perform login and set the token

    foreach ($archiveTasks as $task) {
      try {
        if (!$this->gateway->statusAndScheduleCheck($task)) {
            // Skip this task if it does not meet the status and schedule criteria
            continue;
        }

        // Retrieve the desired supann status from the main task
        $desiredSupannStatus = $this->getArchiveTaskBehaviorFromMainTask($task['fdtasksgranularmaster'][0]);

        // Retrieve the current supann status of the user
        $currentSupannStatus = $this->getUserSupannAccountStatus($task['fdtasksgranulardn'][0]);

        // Check if the current supann status matches the desired status
        if (!$this->isSupannStatusMatching($desiredSupannStatus, $currentSupannStatus)) {
            // The task does not meet the criteria for archiving and can therefore be suppressed
            $result[$task['dn']]['result'] = "User does not meet the criteria for archiving.";
            $this->gateway->removeSubTask($task['dn']);
            continue;
        }

        // Set the archive endpoint and method using the same WebServiceCall object
        $archiveUrl = $_ENV['FUSION_DIRECTORY_API_URL'] . '/archive/user/' . rawurlencode($task['fdtasksgranulardn'][0]);
        $webServiceCall->setCurlSettings($archiveUrl, NULL, 'POST'); // Update settings for the archive request
        $response = $webServiceCall->execute();

        // Check if the HTTP status code is 204
        if ($webServiceCall->getHttpStatusCode() === 204) {
            $result[$task['dn']]['result'] = "User " . $task['fdtasksgranulardn'][0] . " successfully archived.";
            $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '2');
        } else {
            throw new Exception("Unexpected HTTP status code: " . $webServiceCall->getHttpStatusCode());
        }
      } catch (Exception $e) {
            $result[$task['dn']]['result'] = "Error archiving user: " . $e->getMessage();
            $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], $e->getMessage());
      }
    }

      return $result;
  }

    /**
     * @param array|null $data
     * @return array
     * Note: Part of the interface of orchestrator plugin to treat POST method
     */
  public function processEndPointPost (array $data = NULL): array
  {
      return [];
  }

    /**
     * @param array|null $data
     * @return array
     * Note: Part of the interface of orchestrator plugin to treat DELETE method
     */
  public function processEndPointDelete (array $data = NULL): array
  {
      return [];
  }

    /**
     * Retrieve the supannAccountStatus of a user
     * @param string $userDn
     * @return array|null
     */
  private function getUserSupannAccountStatus (string $userDn): ?array
  {
      return $this->gateway->getLdapTasks(
          '(objectClass=supannPerson)',
          ['supannRessourceEtatDate'],
          '',
          $userDn
      );
  }

    /**
     * @param string $taskDN
     * @return array
     * Note: Retrieve the desired supann status from the main task attributes.
     */
  private function getArchiveTaskBehaviorFromMainTask (string $taskDN): array
  {
      return $this->gateway->getLdapTasks(
          '(objectClass=*)',
          ['fdArchiveTaskResource', 'fdArchiveTaskState', 'fdArchiveTaskSubState'],
          '',
          $taskDN
      );
  }

    /**
     * @param array $desiredStatus
     * @param array $currentStatus
     * @return bool
     * Note: Compare the desired supann status with the current status to determine if they match.
     */
  private function isSupannStatusMatching (array $desiredStatus, array $currentStatus): bool
  {
    if (empty($currentStatus[0]['supannressourceetatdate'])) {
        return FALSE;
    }

      // Extract the desired attributes
      $desiredAttributes = $this->extractDesiredAttributes($desiredStatus);

    if (!$desiredAttributes['resource'] || !$desiredAttributes['state']) {
        return FALSE;
    }

      // Check if any of the current supannressourceetatdate values match the desired attributes
    foreach ($currentStatus[0]['supannressourceetatdate'] as $key => $resource) {
      if (!is_numeric($key)) {
          continue;
      }

      if ($this->doesResourceMatch($resource, $desiredAttributes)) {
          return TRUE;
      }
    }

      return FALSE;
  }

  private function extractDesiredAttributes (array $desiredStatus): array
  {
      return [
          'resource' => $desiredStatus[0]['fdarchivetaskresource'][0] ?? NULL,
          'state'    => $desiredStatus[0]['fdarchivetaskstate'][0] ?? NULL,
          'substate' => $desiredStatus[0]['fdarchivetasksubstate'][0] ?? NULL,
      ];
  }

  private function doesResourceMatch (string $resource, array $desiredAttributes): bool
  {
      // Extract parts from the resource string
      $parts = explode(':', $resource);
      $resourcePart = str_replace(['{', '}'], '', $parts[0]);
      $substatePart = $parts[1] ?? '';

      $resourceMatch = $resourcePart === $desiredAttributes['resource'] . $desiredAttributes['state'];
      $substateMatch = empty($desiredAttributes['substate']) || $substatePart === $desiredAttributes['substate'];

      return $resourceMatch && $substateMatch;
  }
}