<?php
declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Controller;
use App\Core\Csrf;
use App\Core\Permission;
use App\Core\TenantContext;
use App\Services\ReleaseTrainService;
use App\Services\ReleaseMilestoneService;
use Throwable;

final class ReleaseTrainController extends Controller
{
    private function organisationId(): int
    {
        return TenantContext::id();
    }

    private function actorId(): int
    {
        return (int) (Auth::user()['id'] ?? 0);
    }

    private function requireWriteAccess(): void
    {
        Auth::requireLogin();
        Permission::require('updates.install');
        Permission::requireSuperAdmin();
        Auth::requireRecentAuthentication();
        Csrf::verify($_POST['_csrf'] ?? null);
    }

    public function index(): void
    {
        Auth::requireLogin();
        Permission::require('updates.view');

        $organisationId = $this->organisationId();
        $service = new ReleaseTrainService();
        $trainId = max(0, (int) ($_GET['train'] ?? 0));
        $currentTrain = $trainId > 0 ? $service->find($trainId, $organisationId) : null;
        $editTrain = isset($_GET['edit']) ? $service->find((int) $_GET['edit'], $organisationId) : null;
        $editStep = ($currentTrain !== null && isset($_GET['step_edit']))
            ? $service->findStep((int) $_GET['step_edit'], $trainId, $organisationId)
            : null;

        $steps = $currentTrain !== null ? $service->stepsWithDependencies($trainId, $organisationId) : [];
        $milestoneService = new ReleaseMilestoneService();
        $milestones = $currentTrain !== null ? $milestoneService->listForTrain($trainId, $organisationId) : [];
        $archivedMilestones = $currentTrain !== null ? array_values(array_filter(
            $milestoneService->listForTrain($trainId, $organisationId, true),
            static fn(array $milestone): bool => $milestone['archived_at'] !== null
        )) : [];
        $milestoneEdit = ($currentTrain !== null && isset($_GET['milestone_edit']))
            ? $milestoneService->find((int) $_GET['milestone_edit'], $trainId, $organisationId)
            : null;

        $this->view('platform/release_trains', [
            'pageTitle' => 'Release Train Manager',
            'result' => $service->paginate($_GET, $organisationId),
            'roadmap' => $service->roadmap($organisationId),
            'dashboard' => $service->executiveDashboard($organisationId),
            'edit' => $editTrain,
            'filters' => $_GET,
            'selected' => $currentTrain,
            'steps' => $steps,
            'progress' => $currentTrain !== null
                ? $service->progress($trainId, $organisationId)
                : ['total' => 0, 'completed' => 0, 'percent' => 0],
            'risk' => $currentTrain !== null
                ? $service->risk($trainId, $organisationId)
                : ['level' => 'none', 'label' => 'Aucun risque', 'blocked' => 0, 'overdue' => 0],
            'stepEdit' => $editStep,
            'milestones' => $milestones,
            'archivedMilestones' => $archivedMilestones,
            'milestoneEdit' => $milestoneEdit,
        ]);
    }

    public function save(): void
    {
        $this->requireWriteAccess();
        try {
            $id = (new ReleaseTrainService())->save(
                $_POST,
                ((int) ($_POST['id'] ?? 0)) ?: null,
                $this->actorId(),
                $this->organisationId()
            );
            $_SESSION['success'] = 'Release train enregistré.';
            $this->redirect('/administration/plateforme/release-trains?train=' . $id);
        } catch (Throwable $exception) {
            $_SESSION['error'] = $exception->getMessage();
            $_SESSION['old'] = $_POST;
            $suffix = !empty($_POST['id']) ? '?edit=' . (int) $_POST['id'] : '';
            $this->redirect('/administration/plateforme/release-trains' . $suffix);
        }
    }

    public function saveStep(): void
    {
        $this->requireWriteAccess();
        $trainId = max(1, (int) ($_POST['release_train_id'] ?? 0));
        try {
            (new ReleaseTrainService())->saveStep(
                $_POST,
                ((int) ($_POST['step_id'] ?? 0)) ?: null,
                $trainId,
                $this->actorId(),
                $this->organisationId()
            );
            $_SESSION['success'] = 'Étape enregistrée.';
        } catch (Throwable $exception) {
            $_SESSION['error'] = $exception->getMessage();
            $_SESSION['step_old'] = $_POST;
        }
        $this->redirect('/administration/plateforme/release-trains?train=' . $trainId);
    }

    public function saveDependency(): void
    {
        $this->requireWriteAccess();
        $trainId = max(1, (int) ($_POST['release_train_id'] ?? 0));
        try {
            (new ReleaseTrainService())->addDependency(
                max(1, (int) ($_POST['step_id'] ?? 0)),
                max(1, (int) ($_POST['depends_on_step_id'] ?? 0)),
                $trainId,
                $this->actorId(),
                $this->organisationId()
            );
            $_SESSION['success'] = 'Dépendance ajoutée.';
        } catch (Throwable $exception) {
            $_SESSION['error'] = $exception->getMessage();
        }
        $this->redirect('/administration/plateforme/release-trains?train=' . $trainId);
    }

    public function archive(): void
    {
        $this->requireWriteAccess();
        try {
            (new ReleaseTrainService())->archive(
                (int) ($_POST['id'] ?? 0),
                $this->actorId(),
                $this->organisationId()
            );
            $_SESSION['success'] = 'Release train archivé.';
        } catch (Throwable $exception) {
            $_SESSION['error'] = $exception->getMessage();
        }
        $this->redirect('/administration/plateforme/release-trains');
    }

    public function archiveStep(): void
    {
        $this->requireWriteAccess();
        $trainId = max(1, (int) ($_POST['release_train_id'] ?? 0));
        try {
            (new ReleaseTrainService())->archiveStep(
                (int) ($_POST['step_id'] ?? 0),
                $trainId,
                $this->actorId(),
                $this->organisationId()
            );
            $_SESSION['success'] = 'Étape archivée.';
        } catch (Throwable $exception) {
            $_SESSION['error'] = $exception->getMessage();
        }
        $this->redirect('/administration/plateforme/release-trains?train=' . $trainId);
    }

    public function archiveDependency(): void
    {
        $this->requireWriteAccess();
        $trainId = max(1, (int) ($_POST['release_train_id'] ?? 0));
        try {
            (new ReleaseTrainService())->archiveDependency(
                max(1, (int) ($_POST['dependency_id'] ?? 0)),
                $trainId,
                $this->actorId(),
                $this->organisationId()
            );
            $_SESSION['success'] = 'Dépendance retirée.';
        } catch (Throwable $exception) {
            $_SESSION['error'] = $exception->getMessage();
        }
        $this->redirect('/administration/plateforme/release-trains?train=' . $trainId);
    }

    public function moveStep(): void
    {
        $this->requireWriteAccess();
        header('Content-Type: application/json; charset=UTF-8');

        $trainId = max(1, (int) ($_POST['release_train_id'] ?? 0));
        $stepId = max(1, (int) ($_POST['step_id'] ?? 0));
        $status = trim((string) ($_POST['status'] ?? ''));

        try {
            $result = (new ReleaseTrainService())->moveStep(
                $stepId,
                $trainId,
                $status,
                $this->actorId(),
                $this->organisationId()
            );
            http_response_code(200);
            echo json_encode([
                'ok' => true,
                'message' => 'Étape déplacée.',
                'step' => $result['step'],
                'progress' => $result['progress'],
                'risk' => $result['risk'],
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        } catch (Throwable $exception) {
            http_response_code(422);
            echo json_encode([
                'ok' => false,
                'message' => $exception->getMessage(),
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }
        exit;
    }

    public function saveMilestone(): void
    {
        $this->requireWriteAccess();
        $trainId = max(1, (int) ($_POST['release_train_id'] ?? 0));
        try {
            (new ReleaseMilestoneService())->save(
                $_POST,
                ((int) ($_POST['milestone_id'] ?? 0)) ?: null,
                $trainId,
                $this->actorId(),
                $this->organisationId()
            );
            $_SESSION['success'] = 'Jalon enregistré.';
        } catch (Throwable $exception) {
            $_SESSION['error'] = $exception->getMessage();
            $_SESSION['milestone_old'] = $_POST;
        }
        $this->redirect('/administration/plateforme/release-trains?train=' . $trainId);
    }

    public function archiveMilestone(): void
    {
        $this->requireWriteAccess();
        $trainId = max(1, (int) ($_POST['release_train_id'] ?? 0));
        try {
            (new ReleaseMilestoneService())->archive(
                max(1, (int) ($_POST['milestone_id'] ?? 0)),
                $trainId,
                $this->actorId(),
                $this->organisationId()
            );
            $_SESSION['success'] = 'Jalon archivé.';
        } catch (Throwable $exception) {
            $_SESSION['error'] = $exception->getMessage();
        }
        $this->redirect('/administration/plateforme/release-trains?train=' . $trainId);
    }

    public function restoreMilestone(): void
    {
        $this->requireWriteAccess();
        $trainId = max(1, (int) ($_POST['release_train_id'] ?? 0));
        try {
            (new ReleaseMilestoneService())->restore(
                max(1, (int) ($_POST['milestone_id'] ?? 0)),
                $trainId,
                $this->actorId(),
                $this->organisationId()
            );
            $_SESSION['success'] = 'Jalon restauré.';
        } catch (Throwable $exception) {
            $_SESSION['error'] = $exception->getMessage();
        }
        $this->redirect('/administration/plateforme/release-trains?train=' . $trainId);
    }

}
