<?php

declare(strict_types=1);

namespace App\Services\Auth;

use App\Models\SupportAccessGrant;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Support\Audit\AuditActionId;
use App\Support\Auth\WorkspaceRole;
use Carbon\CarbonImmutable;
use Illuminate\Database\Eloquent\Collection;
use InvalidArgumentException;

class SupportAccessGrantResolver
{
    public function __construct(
        private readonly WorkspaceAuditLogger $auditLogger,
    ) {}

    public function maxTtlMinutes(): int
    {
        return max(1, (int) config('tenantpilot.support_access.max_ttl_minutes', 240));
    }

    public function validateScope(string $scope): string
    {
        $normalized = trim($scope);

        if (! in_array($normalized, SupportAccessGrant::scopes(), true)) {
            throw new InvalidArgumentException('Unsupported support-access scope.');
        }

        return $normalized;
    }

    public function approvalModeFor(Workspace $workspace, string $scope): string
    {
        $scope = $this->validateScope($scope);

        if ($scope === SupportAccessGrant::SCOPE_AUDIT_VIEW) {
            return SupportAccessGrant::APPROVAL_MODE_AUTO;
        }

        return $this->workspaceHasOwners($workspace)
            ? SupportAccessGrant::APPROVAL_MODE_OWNER_REQUIRED
            : SupportAccessGrant::APPROVAL_MODE_OWNERLESS_WAIVER;
    }

    public function workspaceHasOwners(Workspace $workspace): bool
    {
        return WorkspaceMembership::query()
            ->where('workspace_id', (int) $workspace->getKey())
            ->where('role', WorkspaceRole::Owner->value)
            ->exists();
    }

    public function activeGrantFor(Workspace $workspace, string $scope): ?SupportAccessGrant
    {
        $scope = $this->validateScope($scope);

        $this->expireStaleActiveGrants($workspace);

        return SupportAccessGrant::query()
            ->with(['requester', 'approver'])
            ->where('workspace_id', (int) $workspace->getKey())
            ->where('scope', $scope)
            ->where('status', SupportAccessGrant::STATUS_ACTIVE)
            ->where('expires_at', '>', CarbonImmutable::now())
            ->latest('id')
            ->first();
    }

    public function activeRecoveryGrantFor(Workspace $workspace): ?SupportAccessGrant
    {
        return $this->activeGrantFor($workspace, SupportAccessGrant::SCOPE_WORKSPACE_RECOVERY);
    }

    public function hasActiveRecoveryGrant(Workspace $workspace): bool
    {
        return $this->activeRecoveryGrantFor($workspace) instanceof SupportAccessGrant;
    }

    public function currentOpenGrantFor(Workspace $workspace): ?SupportAccessGrant
    {
        $this->expireStaleActiveGrants($workspace);

        return SupportAccessGrant::query()
            ->with(['requester', 'approver'])
            ->where('workspace_id', (int) $workspace->getKey())
            ->open()
            ->orderByRaw("CASE WHEN status = 'active' THEN 0 ELSE 1 END")
            ->orderByDesc('id')
            ->first();
    }

    /**
     * @return Collection<int, SupportAccessGrant>
     */
    public function pendingRecoveryRequestsFor(Workspace $workspace): Collection
    {
        $this->expireStaleActiveGrants($workspace);

        return SupportAccessGrant::query()
            ->with(['requester'])
            ->where('workspace_id', (int) $workspace->getKey())
            ->where('scope', SupportAccessGrant::SCOPE_WORKSPACE_RECOVERY)
            ->where('status', SupportAccessGrant::STATUS_REQUESTED)
            ->orderBy('requested_at')
            ->get();
    }

    /**
     * @return array<string, mixed>
     */
    public function summaryFor(Workspace $workspace): array
    {
        $grant = $this->currentOpenGrantFor($workspace);

        if (! $grant instanceof SupportAccessGrant) {
            return [
                'status' => 'none',
                'status_label' => 'No active support access',
                'status_color' => 'gray',
                'scope' => null,
                'scope_label' => null,
                'grant_id' => null,
                'reason' => null,
                'requester_label' => null,
                'approval_mode' => null,
                'approval_label' => null,
                'approver_label' => null,
                'requested_ttl_label' => null,
                'expires_at' => null,
                'expires_label' => null,
                'needs_break_glass' => false,
            ];
        }

        return [
            'status' => (string) $grant->status,
            'status_label' => SupportAccessGrant::statusLabel((string) $grant->status),
            'status_color' => $grant->status === SupportAccessGrant::STATUS_ACTIVE ? 'success' : 'warning',
            'scope' => (string) $grant->scope,
            'scope_label' => SupportAccessGrant::scopeLabel((string) $grant->scope),
            'grant_id' => (int) $grant->getKey(),
            'reason' => (string) $grant->reason,
            'requester_label' => $grant->requester?->name ?? $grant->requester?->email ?? 'Platform support',
            'approval_mode' => (string) $grant->approval_mode,
            'approval_label' => SupportAccessGrant::approvalModeLabel((string) $grant->approval_mode),
            'approver_label' => $grant->approver?->name ?? $grant->approver?->email,
            'requested_ttl_label' => $grant->ttl_minutes.' minutes',
            'expires_at' => $grant->expires_at,
            'expires_label' => $grant->expires_at?->diffForHumans(),
            'needs_break_glass' => $grant->needsBreakGlass(),
        ];
    }

    public function expireStaleActiveGrants(?Workspace $workspace = null): void
    {
        $query = SupportAccessGrant::query()
            ->with(['workspace', 'requester'])
            ->where('status', SupportAccessGrant::STATUS_ACTIVE)
            ->whereNotNull('expires_at')
            ->where('expires_at', '<=', CarbonImmutable::now());

        if ($workspace instanceof Workspace) {
            $query->where('workspace_id', (int) $workspace->getKey());
        }

        $query->get()->each(function (SupportAccessGrant $grant): void {
            $grant->forceFill([
                'status' => SupportAccessGrant::STATUS_EXPIRED,
            ])->save();

            $workspace = $grant->workspace;

            if (! $workspace instanceof Workspace) {
                return;
            }

            $this->auditLogger->log(
                workspace: $workspace,
                action: AuditActionId::SupportAccessExpired,
                context: $this->auditContext($grant),
                actor: null,
                status: 'success',
                resourceType: 'support_access_grant',
                resourceId: (string) $grant->getKey(),
                targetLabel: $this->targetLabel($grant),
                summary: 'Support access expired for '.$workspace->name,
            );
        });
    }

    /**
     * @return array<string, mixed>
     */
    public function auditContext(SupportAccessGrant $grant, array $extra = []): array
    {
        return [
            'support_access_grant_id' => (int) $grant->getKey(),
            'workspace_id' => (int) $grant->workspace_id,
            'scope' => (string) $grant->scope,
            'scope_label' => SupportAccessGrant::scopeLabel((string) $grant->scope),
            'status' => (string) $grant->status,
            'approval_mode' => (string) $grant->approval_mode,
            'reason' => (string) $grant->reason,
            'waiver_reason' => $grant->waiver_reason,
            'ttl_minutes' => (int) $grant->ttl_minutes,
            'requested_by_platform_user_id' => (int) $grant->requested_by_platform_user_id,
            'approved_by_user_id' => is_numeric($grant->approved_by_user_id) ? (int) $grant->approved_by_user_id : null,
            'requested_at' => $grant->requested_at?->toISOString(),
            'approved_at' => $grant->approved_at?->toISOString(),
            'starts_at' => $grant->starts_at?->toISOString(),
            'expires_at' => $grant->expires_at?->toISOString(),
            'ended_at' => $grant->ended_at?->toISOString(),
            'denied_at' => $grant->denied_at?->toISOString(),
        ] + $extra;
    }

    public function targetLabel(SupportAccessGrant $grant): string
    {
        return SupportAccessGrant::scopeLabel((string) $grant->scope).' #'.$grant->getKey();
    }
}
