<?php

declare(strict_types=1);

namespace App\Services\Entitlements;

use App\Models\AuditLog;
use App\Models\ManagedEnvironment;
use App\Models\Workspace;
use App\Models\WorkspaceSetting;
use App\Support\Audit\AuditActionId;
use Carbon\CarbonInterface;

final class WorkspaceCommercialLifecycleResolver
{
    public const SETTING_DOMAIN = WorkspaceEntitlementResolver::SETTING_DOMAIN;

    public const SETTING_COMMERCIAL_LIFECYCLE_STATE = 'commercial_lifecycle_state';

    public const SETTING_COMMERCIAL_LIFECYCLE_REASON = 'commercial_lifecycle_reason';

    public const STATE_TRIAL = 'trial';

    public const STATE_GRACE = 'grace';

    public const STATE_ACTIVE_PAID = 'active_paid';

    public const STATE_SUSPENDED_READ_ONLY = 'suspended_read_only';

    public const SOURCE_DEFAULT_ACTIVE_PAID = 'default_active_paid';

    public const SOURCE_WORKSPACE_SETTING = 'workspace_setting';

    public const SOURCE_WORKSPACE_SUBSCRIPTION = WorkspaceSubscriptionResolver::SOURCE_WORKSPACE_SUBSCRIPTION;

    public const ACTION_MANAGED_TENANT_ACTIVATION = 'managed_tenant_activation';

    public const ACTION_REVIEW_PACK_START = 'review_pack_start';

    public const ACTION_REVIEW_HISTORY_READ = 'review_history_read';

    public const ACTION_EVIDENCE_READ = 'evidence_read';

    public const ACTION_GENERATED_PACK_READ = 'generated_pack_read';

    public const OUTCOME_ALLOW = 'allow';

    public const OUTCOME_WARN = 'warn';

    public const OUTCOME_BLOCK = 'block';

    public const OUTCOME_ALLOW_READ_ONLY = 'allow_read_only';

    public const REASON_FAMILY_ENTITLEMENT_SUBSTRATE = 'entitlement_substrate';

    public const REASON_FAMILY_COMMERCIAL_LIFECYCLE = 'commercial_lifecycle';

    public const REASON_FAMILY_WORKSPACE_CLOSURE = 'workspace_closure';

    public const REASON_FAMILY_TENANT_WORKSPACE_REMOVAL = 'tenant_workspace_removal';

    public function __construct(
        private readonly WorkspaceEntitlementResolver $workspaceEntitlementResolver,
        private readonly WorkspaceSubscriptionResolver $workspaceSubscriptionResolver,
    ) {}

    /**
     * @return list<string>
     */
    public static function stateIds(): array
    {
        return [
            self::STATE_TRIAL,
            self::STATE_GRACE,
            self::STATE_ACTIVE_PAID,
            self::STATE_SUSPENDED_READ_ONLY,
        ];
    }

    /**
     * @return array<string, string>
     */
    public static function stateLabels(): array
    {
        return [
            self::STATE_TRIAL => 'Trial',
            self::STATE_GRACE => 'Grace',
            self::STATE_ACTIVE_PAID => 'Active paid',
            self::STATE_SUSPENDED_READ_ONLY => 'Suspended / read-only',
        ];
    }

    /**
     * @return array<string, string>
     */
    public static function stateDescriptions(): array
    {
        return [
            self::STATE_TRIAL => 'Expansion and review-pack starts are available while the workspace is in trial.',
            self::STATE_GRACE => 'New managed-tenant activation is frozen, but review-pack starts remain available with a warning.',
            self::STATE_ACTIVE_PAID => 'Expansion and review-pack starts are available for the active paid workspace.',
            self::STATE_SUSPENDED_READ_ONLY => 'New activation and review-pack starts are blocked while existing review, evidence, and generated-pack access remains read-only.',
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function summary(Workspace $workspace): array
    {
        $lifecycle = $this->resolve($workspace);

        return $lifecycle + [
            'entitlement_summary' => $this->workspaceEntitlementResolver->summary($workspace),
            'action_decisions' => [
                self::ACTION_MANAGED_TENANT_ACTIVATION => $this->actionDecision($workspace, self::ACTION_MANAGED_TENANT_ACTIVATION, $lifecycle),
                self::ACTION_REVIEW_PACK_START => $this->actionDecision($workspace, self::ACTION_REVIEW_PACK_START, $lifecycle),
                self::ACTION_REVIEW_HISTORY_READ => $this->actionDecision($workspace, self::ACTION_REVIEW_HISTORY_READ, $lifecycle),
                self::ACTION_EVIDENCE_READ => $this->actionDecision($workspace, self::ACTION_EVIDENCE_READ, $lifecycle),
                self::ACTION_GENERATED_PACK_READ => $this->actionDecision($workspace, self::ACTION_GENERATED_PACK_READ, $lifecycle),
            ],
        ];
    }

    /**
     * @return array{
     *     workspace_id: int,
     *     state: string,
     *     state_label: string,
     *     source: string,
     *     source_label: string,
     *     rationale: string|null,
     *     description: string,
     *     last_changed_at: CarbonInterface|null,
     *     last_changed_by: string|null
     * }
     */
    public function resolve(Workspace $workspace): array
    {
        $subscriptionSummary = $this->workspaceSubscriptionResolver->summary($workspace);

        $state = (string) $subscriptionSummary['derived_lifecycle_state'];
        $source = (string) $subscriptionSummary['source'];

        $labels = self::stateLabels();
        $descriptions = self::stateDescriptions();
        $lastChanged = $this->lastChangedMetadata($workspace, $source);

        return [
            'workspace_id' => (int) $workspace->getKey(),
            'state' => $state,
            'state_label' => $labels[$state],
            'source' => $source,
            'source_label' => match ($source) {
                self::SOURCE_WORKSPACE_SUBSCRIPTION => 'workspace subscription',
                self::SOURCE_WORKSPACE_SETTING => 'workspace setting',
                default => 'default active paid',
            },
            'rationale' => $subscriptionSummary['status_reason'] ?? null,
            'description' => $descriptions[$state],
            'last_changed_at' => $lastChanged['last_changed_at'],
            'last_changed_by' => $lastChanged['last_changed_by'],
            'subscription_present' => (bool) ($subscriptionSummary['subscription_present'] ?? false),
            'fallback_status' => (bool) ($subscriptionSummary['fallback_status'] ?? true),
            'subscription_state' => $subscriptionSummary['state'] ?? null,
            'subscription_state_label' => $subscriptionSummary['label'] ?? null,
            'subscription_billing_reference' => $subscriptionSummary['billing_reference'] ?? null,
            'subscription_key_date_label' => $subscriptionSummary['key_date_label'] ?? null,
            'subscription_key_date' => $subscriptionSummary['key_date'] ?? null,
            'subscription_needs_review' => (bool) ($subscriptionSummary['needs_review'] ?? false),
            'workspace_closed' => $workspace->isClosed(),
            'workspace_closed_at' => $workspace->closed_at,
            'workspace_closure_reason' => $workspace->closureReason(),
        ];
    }

    /**
     * @param  array<string, mixed>|null  $lifecycle
     * @return array<string, mixed>
     */
    public function actionDecision(Workspace $workspace, string $actionKey, ?array $lifecycle = null): array
    {
        $lifecycle ??= $this->resolve($workspace);

        return match ($actionKey) {
            self::ACTION_MANAGED_TENANT_ACTIVATION => $this->managedTenantActivationDecision($workspace, $lifecycle),
            self::ACTION_REVIEW_PACK_START => $this->reviewPackStartDecision($workspace, $lifecycle),
            self::ACTION_REVIEW_HISTORY_READ,
            self::ACTION_EVIDENCE_READ,
            self::ACTION_GENERATED_PACK_READ => $this->readOnlyDecision($actionKey, $lifecycle),
            default => throw new \InvalidArgumentException(sprintf('Unknown commercial lifecycle action key: %s', $actionKey)),
        };
    }

    /**
     * @return array<string, mixed>
     */
    public function reviewPackStartDecisionForTenant(ManagedEnvironment $tenant): array
    {
        $tenant->loadMissing('workspace');

        if ($tenant->isRemovedFromWorkspace()) {
            return $this->decision(
                lifecycle: $this->resolve($tenant->workspace),
                actionKey: self::ACTION_REVIEW_PACK_START,
                outcome: self::OUTCOME_BLOCK,
                reasonFamily: self::REASON_FAMILY_TENANT_WORKSPACE_REMOVAL,
                message: 'This tenant was removed from the workspace. New review-pack starts are blocked until the tenant is restored to the workspace.',
                substrateDecision: null,
            );
        }

        return $this->actionDecision($tenant->workspace, self::ACTION_REVIEW_PACK_START);
    }

    /**
     * @param  array<string, mixed>  $lifecycle
     * @return array<string, mixed>
     */
    private function managedTenantActivationDecision(Workspace $workspace, array $lifecycle): array
    {
        if ($workspace->isClosed()) {
            return $this->closedWorkspaceDecision(
                $lifecycle,
                self::ACTION_MANAGED_TENANT_ACTIVATION,
                'This workspace is closed. New managed-tenant activation is blocked, while existing review, evidence, and operation history remains available under current RBAC.',
            );
        }

        $substrateDecision = $this->workspaceEntitlementResolver->resolve(
            $workspace,
            WorkspaceEntitlementResolver::KEY_MANAGED_TENANT_ACTIVATION_LIMIT,
        );

        if ((bool) ($substrateDecision['is_blocked'] ?? false)) {
            return $this->decision(
                lifecycle: $lifecycle,
                actionKey: self::ACTION_MANAGED_TENANT_ACTIVATION,
                outcome: self::OUTCOME_BLOCK,
                reasonFamily: self::REASON_FAMILY_ENTITLEMENT_SUBSTRATE,
                message: (string) ($substrateDecision['block_reason'] ?? 'Workspace entitlement currently blocks managed tenant activation.'),
                substrateDecision: $substrateDecision,
            );
        }

        return match ($lifecycle['state']) {
            self::STATE_GRACE => $this->decision(
                lifecycle: $lifecycle,
                actionKey: self::ACTION_MANAGED_TENANT_ACTIVATION,
                outcome: self::OUTCOME_BLOCK,
                reasonFamily: self::REASON_FAMILY_COMMERCIAL_LIFECYCLE,
                message: $this->lifecycleMessage($lifecycle, 'New managed-tenant activation is frozen while this workspace is in grace.'),
                substrateDecision: $substrateDecision,
            ),
            self::STATE_SUSPENDED_READ_ONLY => $this->decision(
                lifecycle: $lifecycle,
                actionKey: self::ACTION_MANAGED_TENANT_ACTIVATION,
                outcome: self::OUTCOME_BLOCK,
                reasonFamily: self::REASON_FAMILY_COMMERCIAL_LIFECYCLE,
                message: $this->lifecycleMessage($lifecycle, 'This workspace is suspended / read-only. New managed-tenant activation is blocked, but existing review and evidence history remains available.'),
                substrateDecision: $substrateDecision,
            ),
            default => $this->decision(
                lifecycle: $lifecycle,
                actionKey: self::ACTION_MANAGED_TENANT_ACTIVATION,
                outcome: self::OUTCOME_ALLOW,
                reasonFamily: null,
                message: $this->lifecycleMessage($lifecycle, 'Managed-tenant activation is available for this workspace commercial state.'),
                substrateDecision: $substrateDecision,
            ),
        };
    }

    /**
     * @param  array<string, mixed>  $lifecycle
     * @return array<string, mixed>
     */
    private function reviewPackStartDecision(Workspace $workspace, array $lifecycle): array
    {
        if ($workspace->isClosed()) {
            return $this->closedWorkspaceDecision(
                $lifecycle,
                self::ACTION_REVIEW_PACK_START,
                'This workspace is closed. New review-pack starts are blocked, while existing review packs, evidence, and review history remain available under current RBAC.',
            );
        }

        $substrateDecision = $this->workspaceEntitlementResolver->resolve(
            $workspace,
            WorkspaceEntitlementResolver::KEY_REVIEW_PACK_GENERATION_ENABLED,
        );

        if ((bool) ($substrateDecision['is_blocked'] ?? false)) {
            return $this->decision(
                lifecycle: $lifecycle,
                actionKey: self::ACTION_REVIEW_PACK_START,
                outcome: self::OUTCOME_BLOCK,
                reasonFamily: self::REASON_FAMILY_ENTITLEMENT_SUBSTRATE,
                message: (string) ($substrateDecision['block_reason'] ?? 'Workspace entitlement currently blocks review pack generation.'),
                substrateDecision: $substrateDecision,
            );
        }

        return match ($lifecycle['state']) {
            self::STATE_GRACE => $this->decision(
                lifecycle: $lifecycle,
                actionKey: self::ACTION_REVIEW_PACK_START,
                outcome: self::OUTCOME_WARN,
                reasonFamily: self::REASON_FAMILY_COMMERCIAL_LIFECYCLE,
                message: $this->lifecycleMessage($lifecycle, 'Workspace is in grace. Review-pack starts remain available, but managed-tenant expansion is frozen.'),
                substrateDecision: $substrateDecision,
            ),
            self::STATE_SUSPENDED_READ_ONLY => $this->decision(
                lifecycle: $lifecycle,
                actionKey: self::ACTION_REVIEW_PACK_START,
                outcome: self::OUTCOME_BLOCK,
                reasonFamily: self::REASON_FAMILY_COMMERCIAL_LIFECYCLE,
                message: $this->lifecycleMessage($lifecycle, 'This workspace is suspended / read-only. New review-pack starts are blocked, but existing review packs, evidence, and review history remain available.'),
                substrateDecision: $substrateDecision,
            ),
            default => $this->decision(
                lifecycle: $lifecycle,
                actionKey: self::ACTION_REVIEW_PACK_START,
                outcome: self::OUTCOME_ALLOW,
                reasonFamily: null,
                message: $this->lifecycleMessage($lifecycle, 'Review-pack starts are available for this workspace commercial state.'),
                substrateDecision: $substrateDecision,
            ),
        };
    }

    /**
     * @param  array<string, mixed>  $lifecycle
     * @return array<string, mixed>
     */
    private function readOnlyDecision(string $actionKey, array $lifecycle): array
    {
        if ((bool) ($lifecycle['workspace_closed'] ?? false)) {
            return $this->decision(
                lifecycle: $lifecycle,
                actionKey: $actionKey,
                outcome: self::OUTCOME_ALLOW_READ_ONLY,
                reasonFamily: self::REASON_FAMILY_WORKSPACE_CLOSURE,
                message: $this->lifecycleMessage($lifecycle, 'Closed workspaces keep existing review, evidence, generated-pack, and operation history available under current RBAC.'),
                substrateDecision: null,
            );
        }

        if (($lifecycle['state'] ?? null) === self::STATE_SUSPENDED_READ_ONLY) {
            return $this->decision(
                lifecycle: $lifecycle,
                actionKey: $actionKey,
                outcome: self::OUTCOME_ALLOW_READ_ONLY,
                reasonFamily: self::REASON_FAMILY_COMMERCIAL_LIFECYCLE,
                message: $this->lifecycleMessage($lifecycle, 'Suspended / read-only workspaces keep existing review, evidence, and generated-pack consumption available under current RBAC.'),
                substrateDecision: null,
            );
        }

        return $this->decision(
            lifecycle: $lifecycle,
            actionKey: $actionKey,
            outcome: self::OUTCOME_ALLOW,
            reasonFamily: null,
            message: $this->lifecycleMessage($lifecycle, 'Read-only history remains available under current RBAC.'),
            substrateDecision: null,
        );
    }

    /**
     * @param  array<string, mixed>  $lifecycle
     */
    private function lifecycleMessage(array $lifecycle, string $message): string
    {
        return sprintf('%s Commercial source: %s.', $message, $this->commercialSourceDescriptor($lifecycle));
    }

    /**
     * @param  array<string, mixed>  $lifecycle
     * @return array<string, mixed>
     */
    private function closedWorkspaceDecision(array $lifecycle, string $actionKey, string $message): array
    {
        return $this->decision(
            lifecycle: array_merge($lifecycle, ['workspace_closed' => true]),
            actionKey: $actionKey,
            outcome: self::OUTCOME_BLOCK,
            reasonFamily: self::REASON_FAMILY_WORKSPACE_CLOSURE,
            message: $this->lifecycleMessage($lifecycle, $message),
            substrateDecision: null,
        );
    }

    /**
     * @param  array<string, mixed>  $lifecycle
     */
    private function commercialSourceDescriptor(array $lifecycle): string
    {
        return ($lifecycle['source'] ?? null) === self::SOURCE_WORKSPACE_SUBSCRIPTION
            ? 'subscription-backed'
            : 'fallback-backed';
    }

    /**
     * @param  array<string, mixed>  $lifecycle
     * @param  array<string, mixed>|null  $substrateDecision
     * @return array<string, mixed>
     */
    private function decision(
        array $lifecycle,
        string $actionKey,
        string $outcome,
        ?string $reasonFamily,
        string $message,
        ?array $substrateDecision,
    ): array {
        return [
            'workspace_id' => (int) ($lifecycle['workspace_id'] ?? 0),
            'action_key' => $actionKey,
            'outcome' => $outcome,
            'is_blocked' => $outcome === self::OUTCOME_BLOCK,
            'is_warning' => $outcome === self::OUTCOME_WARN,
            'block_reason' => $outcome === self::OUTCOME_BLOCK ? $message : null,
            'warning_reason' => $outcome === self::OUTCOME_WARN ? $message : null,
            'message' => $message,
            'reason_family' => $reasonFamily,
            'state' => (string) $lifecycle['state'],
            'state_label' => (string) $lifecycle['state_label'],
            'source' => (string) $lifecycle['source'],
            'source_label' => (string) $lifecycle['source_label'],
            'rationale' => $lifecycle['rationale'] ?? null,
            'workspace_closed' => (bool) ($lifecycle['workspace_closed'] ?? false),
            'workspace_closed_at' => $lifecycle['workspace_closed_at'] ?? null,
            'workspace_closure_reason' => $lifecycle['workspace_closure_reason'] ?? null,
            'entitlement_decision' => $substrateDecision,
        ];
    }

    /**
     * @return array{last_changed_at: CarbonInterface|null, last_changed_by: string|null}
     */
    private function lastChangedMetadata(Workspace $workspace, string $source): array
    {
        if ($source === self::SOURCE_WORKSPACE_SUBSCRIPTION) {
            $audit = AuditLog::query()
                ->where('workspace_id', (int) $workspace->getKey())
                ->where('action', AuditActionId::WorkspaceSubscriptionUpdated->value)
                ->where('resource_type', 'workspace_subscription')
                ->latest('recorded_at')
                ->latest('id')
                ->first();

            if ($audit instanceof AuditLog) {
                return [
                    'last_changed_at' => $audit->recorded_at,
                    'last_changed_by' => $audit->actorDisplayLabel(),
                ];
            }

            $workspace->loadMissing('subscription');

            if ($workspace->subscription !== null) {
                return [
                    'last_changed_at' => $workspace->subscription->updated_at,
                    'last_changed_by' => null,
                ];
            }
        }

        $audit = AuditLog::query()
            ->where('workspace_id', (int) $workspace->getKey())
            ->where('action', AuditActionId::WorkspaceSettingUpdated->value)
            ->where('resource_type', 'workspace_setting')
            ->where('resource_id', self::SETTING_DOMAIN.'.'.self::SETTING_COMMERCIAL_LIFECYCLE_STATE)
            ->latest('recorded_at')
            ->latest('id')
            ->first();

        if ($audit instanceof AuditLog) {
            return [
                'last_changed_at' => $audit->recorded_at,
                'last_changed_by' => $audit->actorDisplayLabel(),
            ];
        }

        $record = WorkspaceSetting::query()
            ->where('workspace_id', (int) $workspace->getKey())
            ->where('domain', self::SETTING_DOMAIN)
            ->whereIn('key', [
                self::SETTING_COMMERCIAL_LIFECYCLE_STATE,
                self::SETTING_COMMERCIAL_LIFECYCLE_REASON,
            ])
            ->with('updatedByUser:id,name')
            ->latest('updated_at')
            ->latest('id')
            ->first();

        if (! $record instanceof WorkspaceSetting) {
            return [
                'last_changed_at' => null,
                'last_changed_by' => null,
            ];
        }

        return [
            'last_changed_at' => $record->updated_at,
            'last_changed_by' => $record->updatedByUser?->name,
        ];
    }
}
