<?php

declare(strict_types=1);

/**
 * APO-194 Card 1 — Navigation Versions action service.
 *
 * Saved navigation versions are operator-named navigation configurations. The
 * service exposes 7 verbs matching the existing admin POST handlers:
 * create / update (settings payload) / activate / deactivate / set-default
 * / duplicate / delete. Both the admin form path and the
 * /api/v1/design/navigation/versions/... API path call this service.
 */
final class MyronNavigationVersionActionService
{
    public function __construct(
        private readonly MyronNavigationDesignRepository $repository,
    ) {
    }

    /**
     * @return array<string, mixed>
     */
    public function create(string $name, MyronActionCallerContext $caller): array
    {
        $name = trim($name);
        if ($name === '') {
            $name = 'New Navigation';
        }
        return (array) $this->repository->createVersion($name, [], $this->callerUserId($caller));
    }

    /**
     * @param array<string, mixed> $settings
     * @return array<string, mixed>
     */
    public function update(string $versionId, array $settings, MyronActionCallerContext $caller): array
    {
        $versionId = $this->requireVersionId($versionId);
        return (array) $this->repository->saveVersion($versionId, $settings, $this->callerUserId($caller));
    }

    /**
     * @return array{versionId: string, isActivated: bool}
     */
    public function activate(string $versionId, MyronActionCallerContext $caller): array
    {
        $versionId = $this->requireVersionId($versionId);
        $this->repository->saveActivation($versionId, true, $this->callerUserId($caller));
        return ['versionId' => $versionId, 'isActivated' => true];
    }

    /**
     * @return array{versionId: string, isActivated: bool}
     */
    public function deactivate(string $versionId, MyronActionCallerContext $caller): array
    {
        $versionId = $this->requireVersionId($versionId);
        $current = (string) ($this->repository->getDefault()['id'] ?? '');
        if ($current === $versionId) {
            throw new MyronActionPreconditionError(
                'Cannot deactivate the current global default navigation version. Set a different default first.'
            );
        }
        $this->repository->saveActivation($versionId, false, $this->callerUserId($caller));
        return ['versionId' => $versionId, 'isActivated' => false];
    }

    /**
     * @return array{versionId: string, previousDefault: string}
     */
    public function setDefault(string $versionId, MyronActionCallerContext $caller): array
    {
        $versionId = $this->requireVersionId($versionId);
        $previousDefault = (string) ($this->repository->getDefault()['id'] ?? '');
        $this->repository->setDefault($versionId, $this->callerUserId($caller));
        return ['versionId' => $versionId, 'previousDefault' => $previousDefault];
    }

    /**
     * @return array<string, mixed>
     */
    public function duplicate(string $versionId, MyronActionCallerContext $caller): array
    {
        $versionId = $this->requireVersionId($versionId);
        return (array) $this->repository->duplicateVersion($versionId, $this->callerUserId($caller));
    }

    /**
     * @return array{versionId: string, deleted: bool}
     * @throws MyronActionPreconditionError when the version is currently bound to a template
     */
    public function delete(string $versionId, MyronActionCallerContext $caller): array
    {
        $versionId = $this->requireVersionId($versionId);
        if (method_exists($this->repository, 'isReferencedByTemplate') && $this->repository->isReferencedByTemplate($versionId)) {
            throw new MyronActionPreconditionError(
                'Navigation version is bound to one or more templates; rebind those templates before deleting.'
            );
        }
        $this->repository->deleteVersion($versionId);
        return ['versionId' => $versionId, 'deleted' => true];
    }

    private function requireVersionId(string $versionId): string
    {
        $versionId = trim($versionId);
        if ($versionId === '') {
            throw new InvalidArgumentException('versionId is required.');
        }
        return $versionId;
    }

    private function callerUserId(MyronActionCallerContext $caller): string
    {
        return $caller->callerType === 'human-session' ? $caller->callerId : '';
    }
}
