<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\AccessControl;
use App\Core\Auth;

/**
 * DIAR workflow separation-of-duties guard.
 *
 * AccessControl::allows() remains the permission boundary. This service adds
 * the investigation-role and case-ownership boundary so a technical Site
 * Administrator does not approve findings merely because the user can manage
 * the platform.
 */
final class DiarWorkflowAuthorization
{
    /** @return array<int,string> */
    public function roleCodes(): array
    {
        $user = Auth::user();
        $roles = is_array($user['access_control']['roles'] ?? null)
            ? $user['access_control']['roles']
            : [];
        $codes = [];
        foreach ($roles as $role) {
            $code = strtoupper(trim((string)($role['role_code'] ?? '')));
            if ($code !== '' && !in_array($code, $codes, true)) {
                $codes[] = $code;
            }
        }
        return $codes;
    }

    public function canAssign(): bool
    {
        return AccessControl::allows('diar.investigation.assign')
            && $this->hasAnyRole(['SITE_ADMIN','DIAR_INV_SUPERVISOR','DIAR_AUDIT_DIRECTOR']);
    }

    /** @param array<string,mixed> $case */
    public function canReview(array $case): bool
    {
        return AccessControl::allows('diar.investigation.review')
            && $this->investigatorOrSeniorOwns($case);
    }

    /** @param array<string,mixed> $case */
    public function canRecordEvidence(array $case): bool
    {
        return AccessControl::allows('diar.investigation.evidence')
            && $this->investigatorOrSeniorOwns($case);
    }

    /** @param array<string,mixed> $case */
    public function canRequestInformation(array $case): bool
    {
        return AccessControl::allows('diar.investigation.request_information')
            && $this->investigatorOrSeniorOwns($case);
    }

    /** @param array<string,mixed> $case */
    public function canProposeFinding(array $case): bool
    {
        return AccessControl::allows('diar.investigation.finding.propose')
            && $this->investigatorOrSeniorOwns($case);
    }

    public function canSupervisorReview(): bool
    {
        return AccessControl::allows('diar.investigation.supervisor_review')
            && $this->hasAnyRole(['DIAR_INV_SUPERVISOR','DIAR_AUDIT_DIRECTOR']);
    }

    public function canManagementApprove(): bool
    {
        return AccessControl::allows('diar.investigation.management_approve')
            && $this->hasAnyRole(['DIAR_AUDIT_DIRECTOR']);
    }

    public function canManageCorrectiveActions(): bool
    {
        return AccessControl::allows('diar.investigation.corrective_action')
            && $this->hasAnyRole(['DIAR_INV_SUPERVISOR','DIAR_AUDIT_DIRECTOR']);
    }

    public function canClose(): bool
    {
        return AccessControl::allows('diar.investigation.close')
            && $this->hasAnyRole(['DIAR_INV_SUPERVISOR','DIAR_AUDIT_DIRECTOR']);
    }

    public function canReopen(): bool
    {
        return AccessControl::allows('diar.investigation.reopen')
            && $this->hasAnyRole(['DIAR_AUDIT_DIRECTOR']);
    }

    /** @param array<string,mixed> $case */
    private function investigatorOrSeniorOwns(array $case): bool
    {
        if ($this->hasAnyRole(['DIAR_INV_SUPERVISOR','DIAR_AUDIT_DIRECTOR'])) {
            return true;
        }
        if (!$this->hasAnyRole(['DIAR_INVESTIGATOR'])) {
            return false;
        }
        $assignedTo = trim((string)($case['assigned_to'] ?? ''));
        $actor = trim((string)Auth::name());
        return $assignedTo !== '' && $actor !== '' && strcasecmp($assignedTo, $actor) === 0;
    }

    /** @param array<int,string> $required */
    private function hasAnyRole(array $required): bool
    {
        return count(array_intersect($this->roleCodes(), $required)) > 0;
    }
}
