<?php

namespace App\Services;

use App\Models\Stage;
use App\Repositories\EvidenceRepository;
use App\Repositories\StageRepository;
use Exception;

class StageService {
    private StageRepository $stageRepository;
    private EvidenceRepository $evidenceRepository;

    public function __construct(){
        $this->stageRepository = new StageRepository();
        $this->evidenceRepository = new EvidenceRepository();
    }

    public function getStages()
    {
        $this->stageRepository->getAll();
    }

    public function getStageById(int $stageId): ?Stage 
    {
        $stage = $this->stageRepository->findBy('id', $stageId);
        $stage->project = $this->stageRepository->getProjectBy($stageId);
        $stage->documents = $this->evidenceRepository->findByStageId($stageId);

        return $stage;
    }

    public function createStage(Stage $stage)
    {
        $stageId = $this->stageRepository->create($stage);
        
        if(!$stageId){
            throw new Exception('Hubo un problema al crear la etapa.', 500);
        }

        $this->createStageFolder($stage->project_id, $stageId, $stage->name, $stage->parent_id);
        
        // Obtener la etapa creada con toda su información
        $createdStage = $this->stageRepository->findBy('id', $stageId);
        
        return $createdStage;
    }

    private function createStageFolder(int $projectId, int $stageId, string $stageName, ?int $parentId): void
    {
        $project = $this->stageRepository->getProjectBy($stageId);
        if (!$project) {
            throw new Exception("No se pudo obtener la información del proyecto.", 500);
        }
        
        $sanitizedProjectName = $this->sanitizeFolderName($project->name);
        $basePath = __DIR__ . '/../../Storage/projects/' . $sanitizedProjectName;
        
        if ($parentId !== null) {
            $parentStage = $this->stageRepository->findBy('id', $parentId);
            if ($parentStage) {
                $parentPath = $this->getStageFullPath($projectId, $parentStage, $project->name);
                $stagePath = $parentPath . '/' . $this->sanitizeFolderName($stageName);
            } else {
                $stagePath = $basePath . '/' . $this->sanitizeFolderName($stageName);
            }
        } else {
            $stagePath = $basePath . '/' . $this->sanitizeFolderName($stageName);
        }
        
        if (!is_dir($stagePath)) {
            if(!mkdir($stagePath, 0777, true)){
                throw new Exception("No se pudo crear la carpeta de la etapa.", 500);
            }
        }
    }

    private function getStageFullPath(int $projectId, Stage $stage, ?string $projectName = null): string
    {
        if ($projectName === null) {
            $project = $this->stageRepository->getProjectBy($stage->id);
            if (!$project) {
                throw new Exception("No se pudo obtener la información del proyecto.", 500);
            }
            $projectName = $project->name;
        }
        
        $sanitizedProjectName = $this->sanitizeFolderName($projectName);
        $basePath = __DIR__ . '/../../Storage/projects/' . $sanitizedProjectName;
        $pathParts = [$this->sanitizeFolderName($stage->name)];
        
        $currentParentId = $stage->parent_id;
        while ($currentParentId !== null) {
            $parentStage = $this->stageRepository->findBy('id', $currentParentId);
            if ($parentStage) {
                array_unshift($pathParts, $this->sanitizeFolderName($parentStage->name));
                $currentParentId = $parentStage->parent_id;
            } else {
                break;
            }
        }
        
        return $basePath . '/' . implode('/', $pathParts);
    }

    private function sanitizeFolderName(string $name): string
    {
        $name = preg_replace('/[<>:"\/\\\\|?*]/', '_', $name);

        $name = trim($name);

        $name = preg_replace('/\s+/', ' ', $name);
        
        return $name;
    }

    public function updateStage(Stage $stage)
    {
        if(!$this->stageRepository->update($stage)){
            throw new Exception("Hubo un problema al actualizar la etapa", 500);
        }
    }

    public function deleteStage(int $stageId)
    {
        $stage = $this->stageRepository->findBy('id', $stageId);
        
        if(!$stage){
            throw new Exception("Etapa no encontrada.", 404);
        }

        $this->deleteStageFolder($stage->project_id, $stage);

        $this->stageRepository->delete($stageId);
    }

    private function deleteStageFolder(int $projectId, Stage $stage): void
    {
        $stagePath = $this->getStageFullPath($projectId, $stage);
        
        if (is_dir($stagePath)) {
            $this->deleteDirectory($stagePath);
        }
    }

    private function deleteDirectory(string $dir): bool
    {
        if (!file_exists($dir)) {
            return true;
        }

        if (!is_dir($dir)) {
            return unlink($dir);
        }

        foreach (scandir($dir) as $item) {
            if ($item == '.' || $item == '..') {
                continue;
            }

            if (!$this->deleteDirectory($dir . DIRECTORY_SEPARATOR . $item)) {
                return false;
            }
        }

        return rmdir($dir);
    }

    public function updateTrafficLight(int $stageId, string $status_traffic_light)
    {
        $validStatuses = ['rojo', 'amarillo', 'verde'];
        
        if (!in_array($status_traffic_light, $validStatuses)) {
            throw new Exception("Estado de semáforo inválido.", 400);
        }

        if(!$this->stageRepository->updateTrafficLight($stageId, $status_traffic_light)){
            throw new Exception("Hubo un problema al actualizar el semáforo de la etapa", 500);
        }
    }

    public function createStageTree(int $projectId, array $structure): bool
    {   
        try {
            $this->insertStageTree($projectId, $structure, null);
            
            return true;
        } catch (\Throwable $e) {
            error_log($e->getMessage());
            return false;
        }
    }

    private function insertStageTree(int $projectId, array $stages, ?int $parentId): void
    {
        foreach ($stages as $stageData) {
            $stage = new Stage([
                'name' => $stageData['name'] ?? '',
                'description' => $stageData['description'] ?? 'pendiente de descripción',
                'start_date' => $stageData['start_date'] ?? null,
                'end_date' => $stageData['end_date'] ?? null,
                'project_id' => $projectId,
                'parent_id' => $parentId,
                'deletable' => $stageData['deletable'] ?? false
            ]);

            $newParentId = $this->stageRepository->create($stage);

            $this->createStageFolder($projectId, $newParentId, $stage->name, $parentId);

            if (!empty($stageData['children']) && is_array($stageData['children'])) {
                $this->insertStageTree($projectId, $stageData['children'], $newParentId);
            }
        }
    }

}