<?php

namespace App\Controllers;

use App\Enums\LocationState;
use App\Enums\ProcessGeneralState;
use App\Helpers\DateHelper;
use App\Models\Modality;
use App\Models\Procedure;
use App\Models\Process;
use App\Models\UserComunity;
use App\Models\UserUgel;
use App\Services\AreaService;
use App\Services\AttachmentService;
use App\Services\FileValidator;
use App\Services\LocationService;
use App\Services\NotificationService;
use App\Services\ProcedureService;
use App\Services\ProcessService;
use App\Services\UserService;
use Config\Database;
use Exception;

class ProcessController extends BaseController
{

    protected ProcessService $processService;
    protected LocationService $locationService;
    protected AttachmentService $attachmentService;
    protected ProcedureService $procedureService;
    protected AreaService $areaService;

    public function __construct() {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        $this->processService = new ProcessService();
        $this->locationService = new LocationService();
        $this->attachmentService = new AttachmentService();
        $this->procedureService = new ProcedureService();
        $this->areaService = new AreaService();

    }

    public function create()
    {

        $role = $_SESSION['role'];
        $procedures = $role === 'COMUNITY' ? $this->procedureService->getVirtualProcedures() : Procedure::getActives();
        $userId = $_SESSION['user_id'];
        $user = UserComunity::findBy('user_id', $userId);
        $this->render('processes/processes-create', ['procedures' => $procedures, 'user' => $user]);
    }

    public function showProcesses()
    {
        if (!isset($_SESSION['user_id'])) {
            $_SESSION['error'] = "No tienes permiso para acceder a esta página.";
            header("Location: " . BASE_HEADER);
            exit;
        }

        try {
            $user = UserService::getUser();

            if (!$user || !$user->group_id) {
                throw new Exception("No se encontró el grupo del usuario.");
            }

            $groupId = $user->group_id;
            $subgroupId = $user->subgroup_id;


            $processes = $this->processService->getDerivedProcessesByGroup($user->group_id, $user->subgroup_id);

            $processes = $this->processService->enrichWithState($processes);

            $this->render('processes/processes-show', ['processes' => $processes, 'group_id' => $groupId, 'subgroup_id' => $subgroupId]);

        } catch (Exception $e) {
            $_SESSION['message'] = "Error: " . $e->getMessage();
            $_SESSION['message_type'] = "danger";
            header("Location: " . BASE_HEADER . "dashboard");
            exit;
        }
    }

    public function showProcess($id)
    {
        $process = Process::find($id);

        if (!$process) {
            $_SESSION['message'] = "Trámite no encontrado.";
            $_SESSION['message_type'] = "danger";
            header("Location: " . BASE_HEADER . "processes/show-processes");
            exit;
        }

        $process = $this->processService->enrichSingle($process);
        $locations = $this->locationService->getLocationsWithRelationsByProcessId($process->id);
        $attachments = $this->attachmentService->getAllAttachmentsByProcess($process->id);
        $procedures = Procedure::getActives();

        $lastLocation = $process->lastLocation();
        $isFirstNode = $lastLocation ? $this->locationService->isFirstNode($lastLocation, $process) : false;

        $this->render('processes/processes-look', ['process' => $process, 'locations' => $locations, 'user' => $process->user(), 'attachments' => $attachments, 'lastLocation' => $lastLocation, 'isFirstNode' => $isFirstNode, 'procedures' => $procedures]);
    }

    public function amend($id)
    {
        $process = Process::find($id);

        if (!$process || $process->general_state !== ProcessGeneralState::OBSERVADO) {
            $_SESSION['message'] = "No puedes editar este trámite.";
            $_SESSION['message_type'] = "danger";
            header("Location: " . BASE_HEADER . "processes/my-processes");
            exit;
        }

        $location = $this->locationService->getFirstLocationByProcessId($process->id);

        $procedures = Procedure::getActives();
        $userId = $_SESSION['user_id'];
        $user = UserComunity::findBy('user_id', $userId);

        $lastLocation = $process->lastLocation();

        $this->render('processes/processes-correct', ['process' =>$process, 'procedures' => $procedures, 'user' => $user, 'location' => $location, 'lastLocation' => $lastLocation]);
    }


    public function store()
    {
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {

            $db = Database::getConnection();

            try {

                $db->beginTransaction();

                if (!empty($_FILES['files']['tmp_name'][0])) {
                    FileValidator::validatePdfFiles($_FILES['files']);
                }

                $session = $_SESSION;
                $data = $_POST;

                $process = $this->processService->createProcess($data, $session);

                $locations = $this->locationService->createInitialLocations($process, $data, $session);

                $lastLocation = end($locations);

                if ($lastLocation) {
                    $this->processService->updateLastLocation($process->id, $lastLocation->id);
                }

                if (!empty($_FILES['files']['name'][0])) { 
                    AttachmentService::uploadFiles($locations[0]->id, $process->num_exped, $_FILES['files']);
                }

                $firstLocation = $locations[0] ?? null;
                $secondLocation = $locations[1] ?? null;

                if ($session['role'] === 'COMUNITY') {
                    NotificationService::createNotification($process->id, "Nuevo trámite N° {$process->num_exped} pendiente.", null, $firstLocation->group_id, $firstLocation->subgroup_id);
                }
                else{

                    if (!empty($secondLocation))
                    {
                        NotificationService::createNotification($process->id, "El trámite {$process->num_exped} ha sido derivado a su equipo.", null, $secondLocation->group_id, $secondLocation->subgroup_id);
                    }
                }

                $db->commit();

                $_SESSION['message'] = "¡Trámite creado exitosamente!";
                $_SESSION['message_type'] = "success";
                $_SESSION['created_process'] = [
                    'id' => $process->id,
                    'num_exped' => $process->num_exped,
                    'entry_date' => date('d/m/Y', strtotime($process->entry_date)),
                    'subject' => $process->subject,
                    'folios' => $process->folios,
                    'procedure' => $process->procedure()->description ?? $process->custom_procedure ?? 'OTROS',
                    'key' => $process->key,
                ];
                
                header("Location: " . BASE_HEADER . "processes/create");
                exit;
            } catch (Exception $e) {
                if ($db->inTransaction()) {
                    $db->rollBack();
                }

                $_SESSION['message'] = "Error: " . $e->getMessage();
                $_SESSION['message_type'] = "danger";
                header("Location: " . BASE_HEADER . "processes/create");
                exit;
            }
        }
    }

    public function deriveProcess($id)
    {
        $process = Process::find($id);
        if (!$process) {
            $_SESSION['message'] = "Error: Trámite no encontrado.";
            $_SESSION['message_type'] = "danger";
            header("Location: " . BASE_HEADER . "processes/show-processes");
            exit;
        }

        $nextRoute = null;

        if($process->derivation_mode == "AUTOMATICO")
        {
            $nextRoute = $this->locationService->getNextRouteNode($process->lastLocation());
        }
        
        $attachments = $this->attachmentService->getAllAttachmentsByProcess($process->id);

        $areas = $this->areaService->getAreasWithRelations();

        $this->render('processes/processes-derive', ['process' => $process, 'attachments' => $attachments, 'nextRoute' => $nextRoute, 'areas' => $areas]);
    }

    public function storeDerive()
    {
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            try {

                $processId = $_POST['process_id'] ?? null;
                $derivationMode = $_POST['derivation_mode_value'] ?? 'AUTOMATICO';

                if (!empty($_FILES['files']['tmp_name'][0])) {
                    FileValidator::validatePdfFiles($_FILES['files']);
                }

                if (!$processId) {
                    $_SESSION['message'] = "Error: Debe completar los campos obligatorios.";
                    $_SESSION['message_type'] = "danger";
                    header("Location: " . BASE_HEADER . "processes/$processId/derive");
                    exit;
                }

                $process = Process::find($processId);
                if (!$process) {
                    $_SESSION['message'] = "Error: Trámite no encontrado.";
                    $_SESSION['message_type'] = "danger";
                    header("Location: " . BASE_HEADER . "processes/$processId/derive");
                    exit;
                }

                if ($derivationMode === 'MANUAL') {
                    $areaId = $_POST['area_id'] ?? null;
                    $groupId = $_POST['group_id'] ?? null;
    
                    if (empty($areaId) || empty($groupId)) {
                        $_SESSION['message'] = "Error: Debe seleccionar al menos el Área y el Grupo en la derivación manual.";
                        $_SESSION['message_type'] = "danger";
                        header("Location: " . BASE_HEADER . "processes/$processId/derive");
                        exit;
                    }
                }

                $currentLocation = $process->lastLocation();
                
                if($currentLocation->state !== LocationState::RECIBIDO)
                {
                    $_SESSION['message'] = "Error: Debes aceptar el trámite antes de derivarlo.";
                    $_SESSION['message_type'] = "danger";
                    header("Location: " . BASE_HEADER . "processes/show-processes");
                    exit;
                }

                if ($currentLocation) {
                    $currentLocation->state = LocationState::DERIVADO;
                    $currentLocation->attended_date = DateHelper::adjustToBusinessDate(date('Y-m-d H:i:s'))->format('Y-m-d H:i:s');
                    if(!empty($_POST['comment']))
                    {
                        $currentLocation->comment = $_POST['comment'];
                    }
                    $currentLocation->save();
                }


                $nextRoute = null;
                $nextGroupId = null;
                $nextSubgroupId = null;

                if ($derivationMode === 'AUTOMATICO') {
                    $nextRoute = $this->locationService->getNextRouteNode($currentLocation);

                    if ($nextRoute) {
                        $nextGroupId = $nextRoute->group_id;
                        $nextSubgroupId = $nextRoute->subgroup_id;
                    } else {
                        $_SESSION['message'] = "No se encontró la siguiente ruta de derivación automática.";
                        $_SESSION['message_type'] = "warning";
                        header("Location: " . BASE_HEADER . "processes/show-processes");
                        exit;
                    }

                } else {
                    $nextGroupId = $_POST['group_id'] ?? null;
                    $nextSubgroupId = !empty($_POST['subgroup_id']) ? $_POST['subgroup_id'] : null;

                    if (!$nextGroupId) {
                        $_SESSION['message'] = "Error: Debe seleccionar un grupo de destino para la derivación manual.";
                        $_SESSION['message_type'] = "danger";
                        header("Location: " . BASE_HEADER . "processes/$processId/derive");
                        exit;
                    }

                    $process->derivation_mode = 'MANUAL';
                    $process->save();
                }

                $state = LocationState::PENDIENTE;
                $derivedDate = date('Y-m-d H:i:s');

                $location = $this->locationService->createLocation(
                    $process->id,
                    $nextGroupId,
                    $nextSubgroupId,
                    $nextRoute ? $nextRoute->id : null,
                    $state,
                    $derivedDate,
                    null,
                    null,
                    null
                );

                $this->processService->updateLastLocation($process->id, $location->id);

                if (!empty($_FILES['files']['name'][0])) { 
                    AttachmentService::uploadFiles($currentLocation->id, $process->num_exped, $_FILES['files']);
                }

                NotificationService::createNotification($process->id, "El trámite {$process->num_exped} ha sido derivado a su equipo.", null, $nextGroupId, $nextSubgroupId);

                $_SESSION['message'] = "¡Trámite derivado exitosamente!";
                $_SESSION['message_type'] = "success";
                header("Location: " . BASE_HEADER . "processes/show-processes");
                exit;

            } catch (Exception $e) {
                $_SESSION['message'] = "Error: " . $e->getMessage();
                $_SESSION['message_type'] = "danger";
                header("Location: " . BASE_HEADER . "processes/$processId/derive");
                exit;
            }
        }
    }

    public function storeAcceptance($processId)
    {
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {

            $process = Process::find($processId);

            if (!$process) {
                $_SESSION['message'] = "Error: Trámite no encontrado.";
                $_SESSION['message_type'] = "danger";
                header("Location: " . BASE_HEADER . "processes/show-processes");
                exit;
            }

            $user = UserUgel::findBy('user_id', $_SESSION['user_id']);
            $currentLocation = $process->lastLocation();

            if($currentLocation->state !== LocationState::PENDIENTE)
            {
                $_SESSION['message'] = "Error: Ya has aceptado este trámite.";
                $_SESSION['message_type'] = "danger";
                header("Location: " . BASE_HEADER . "processes/show-processes");
                exit;
            }
            
            if ($this->locationService->acceptLocation($currentLocation)) {
                NotificationService::markNotificationAsReadByProcess($processId, $user->group_id, $user->subgroup_id);
                $_SESSION['message'] = "¡Trámite aceptado exitosamente!";
                $_SESSION['message_type'] = "success";
            } else {
                $_SESSION['message'] = "Error al aceptar el trámite.";
                $_SESSION['message_type'] = "danger";
            }

            header("Location: " . BASE_HEADER . "processes/$processId/show-process");
            exit;
        }
    }

    public function storeAcceptanceAll()
    {
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $user = UserUgel::findBy('user_id', $_SESSION['user_id']);
            $groupId = $user->group_id;

            if (!$groupId) {
                $_SESSION['message'] = "Error: No se encontró el grupo del usuario.";
                $_SESSION['message_type'] = "danger";
                header("Location: " . BASE_HEADER . "processes/show-processes");
                exit;
            }

            if ($this->locationService->acceptAllLocationsByGroup($groupId, $user->subgroup_id)) {
                $_SESSION['success'] = "Todos los trámites han sido aceptados correctamente.";
            } else {
                $_SESSION['message'] = "No hay trámites para recibir.";
                $_SESSION['message_type'] = "danger";
            }

            header("Location: " . BASE_HEADER . "processes/show-processes");
            exit;
        }
    }

    public function search()
    {
        $modalities = Modality::getActives();
        $periods = $this->processService->getDistinctPeriods();
        $this->render('processes/processes-search-form', ['periods' => $periods, 'modalities' => $modalities]);
    }

    public function searchProcess()
    {
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $modalities = Modality::getActives();
            $periods = $this->processService->getDistinctPeriods();
            $data = $_POST;
            $process = $this->processService->searchProcess($data);

            if (!$process) {
                $_SESSION['message'] = "No se encontró ningún trámite con los datos ingresados.";
                $_SESSION['message_type'] = "warning";
                header("Location: " . BASE_HEADER . "processes/search");
                exit;
            }

            $process = $this->processService->enrichSingle($process);
            $locations = $this->locationService->getLocationsWithRelationsByProcessId($process->id);

            $this->render('processes/processes-search-form', [
                'modalities' => $modalities,
                'periods' => $periods,
                'process' => $process,
                'locations' => $locations,
                'user' => $process->user(),
                'data' => $data
            ]);
        } else {
            header("Location: " . BASE_HEADER . "processes/search");
            exit;
        }
    }


    public function respond($id)
    {
        $process = Process::find($id);

        if (!$process) {
            $_SESSION['message'] = "Trámite no encontrado.";
            $_SESSION['message_type'] = "danger";
            header("Location: " . BASE_HEADER . "processes/show-processes");
            exit;
        }

        $process = $this->processService->enrichSingle($process);

        $this->render('processes/processes-respond', ['process' => $process, 'user' => $process->user()]);
    }

    public function storeResponse()
    {
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            try {

                $process_id = $_POST['process_id'];
                $resolution_number = isset($_POST['resolution_number']) && $_POST['resolution_number'] !== '' ? $_POST['resolution_number'] : null;
                $comment = isset($_POST['comment']) && $_POST['comment'] !== '' ? $_POST['comment'] : null;
                $files = $_FILES['files'] ?? null;
                $response_type = isset($_POST['response_type']) ? $_POST['response_type'] : null;

                if (!empty($_FILES['files']['tmp_name'][0])) {
                    FileValidator::validatePdfFiles($_FILES['files']);
                }

                $this->processService->handleResponse($process_id, $resolution_number, $comment, $files, $response_type);

                $_SESSION['message'] = "Respuesta emitida correctamente.";
                $_SESSION['message_type'] = "success";
                header("Location: " . BASE_HEADER . "processes/show-processes");
            exit;
            } catch (Exception $e) {
                $_SESSION['message'] = "Error: " . $e->getMessage();
                $_SESSION['message_type'] = "danger";
                header("Location: " . BASE_HEADER . "processes/$process_id/respond");
            exit;
            }
        }
    }

    public function myProcesses()
    {
        $userId = $_SESSION['user_id'];
        $user = UserComunity::findBy('user_id', $userId);
        if(!$user)
        {
            $_SESSION['message'] = "No tiene permisos para acceder a esta página.";
            $_SESSION['message_type'] = "warning";
            header("Location: " . BASE_HEADER . "dashboard");
        }
        $processes = $this->processService->getProcessesByUserId($user->id);
        $this->render('processes/user-processes', ['processes' => $processes]);
    }

    public function showResolvedProcesses()
    {
        $userId = $_SESSION['user_id'];
        $user = UserComunity::findBy('user_id', $userId);
        if(!$user)
        {
            $_SESSION['message'] = "No tiene permisos para acceder a esta página.";
            $_SESSION['message_type'] = "warning";
            header("Location: " . BASE_HEADER . "dashboard");
        }
        $resolvedProcesses = $this->processService->getProcessesResolvedByUserId($user->id);

        $this->render('processes/user-resolutions', ['resolvedProcesses' => $resolvedProcesses]);
    }

    public function correct($id)
    {
        if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['_method']) && $_POST['_method'] === 'PUT') {
            try {

                if (!empty($_FILES['files']['tmp_name'][0])) {
                    FileValidator::validatePdfFiles($_FILES['files']);
                }

                $data = $_POST;
                $session = $_SESSION;
            
                $process = $this->processService->correctProcess($id, $data, $session);

                $lastLocation = $process->lastLocation();

                if (!empty($_FILES['files'])) {
                    AttachmentService::uploadFiles($lastLocation->id, $process->num_exped, $_FILES['files']);
                }

                NotificationService::createNotification(
                    $process->id,
                    "El usuario ha subsanado y reingresado el trámite {$process->num_exped}.",
                    null,
                    $lastLocation->group_id,
                    $lastLocation->subgroup_id
                );

                $_SESSION['message'] = "El trámite ha sido subsanado y reingresado correctamente.";
                $_SESSION['message_type'] = "success";

                if ($_SESSION['role'] == "COMUNITY") {
                    header("Location: " . BASE_HEADER . "processes/my-processes");
                } else {
                    header("Location: " . BASE_HEADER . "processes/show-processes");
                }
                exit;
            } catch (Exception $e) {
                $_SESSION['message'] = "Error al subsanar el trámite: " . $e->getMessage();
                $_SESSION['message_type'] = "danger";
                header("Location: " . BASE_HEADER . "processes/$id/amend");
                exit;
            }
        }

        $_SESSION['message'] = "Método no permitido.";
        $_SESSION['message_type'] = "danger";
        header("Location: " . BASE_HEADER . "processes/my-processes");
        exit;
    }

    public function updateProcedure($id)
    {
        header('Content-Type: application/json');

        $procedureId = $_POST['procedure_id'] ?? null;
        $role = $_SESSION['role'];
        $group = $_SESSION['group'] ?? null;

        if (!$procedureId) {
            echo json_encode(['success' => false, 'message' => 'Debe seleccionar un procedimiento válido.']);
            return;
        }

        $process = Process::find($id);
        if (!$process) {
            echo json_encode(['success' => false, 'message' => 'Trámite no encontrado.']);
            return;
        }

        // if (!($role === 'UGEL' && $group === 'MESA DE PARTES') || !($role === 'ADMIN' && $group === 'MESA DE PARTES')) {
        //     echo json_encode(['success' => false, 'message' => 'No tiene permiso para editar este trámite.']);
        //     return;
        // }

        if ($group !== 'MESA DE PARTES' || ($role !== 'UGEL' && $role !== 'ADMIN')) {
            echo json_encode(['success' => false, 'message' => 'No tiene permiso para editar este trámite.']);
            return;
        }
        

        $location = $process->lastLocation();
        if (!$location) {
            echo json_encode(['success' => false, 'message' => 'No se encontró la ubicación actual del trámite.']);
            return;
        }

        if ($location->state !== 'PENDIENTE' && $location->state !== 'RECIBIDO') {
            echo json_encode(['success' => false, 'message' => 'Solo se puede editar un trámite pendiente.']);
            return;
        }

        if (!$this->locationService->isFirstNode($location, $process)) {
            echo json_encode(['success' => false, 'message' => 'Solo puede editar el procedimiento en el primer nodo (Mesa de Partes inicial).']);
            return;
        }

        $process->procedure_id = $procedureId;
        $process->save();

        $firstNode = $this->procedureService->getFirstNode($procedureId);

        if ($firstNode) {
            $location->procedure_route_id = $firstNode->id;
            $location->save();
        }

        echo json_encode(['success' => true, 'message' => 'Procedimiento actualizado correctamente.']);
    }


}