<?php

declare(strict_types=1);

namespace App\Services\Baselines\SnapshotRendering\Renderers;

use App\Models\BaselineSnapshotItem;
use App\Services\Baselines\SnapshotRendering\FidelityState;
use App\Services\Baselines\SnapshotRendering\GapSummary;
use App\Services\Baselines\SnapshotRendering\RenderedAttribute;
use App\Services\Baselines\SnapshotRendering\RenderedSnapshotItem;
use App\Services\Baselines\SnapshotRendering\SnapshotTypeRenderer;
use App\Support\Inventory\InventoryPolicyTypeMeta;
use Illuminate\Support\Str;

class FallbackSnapshotTypeRenderer implements SnapshotTypeRenderer
{
    public function supports(string $policyType): bool
    {
        return false;
    }

    public function render(BaselineSnapshotItem $item): RenderedSnapshotItem
    {
        $meta = $this->meta($item);
        $fidelity = FidelityState::Unsupported;

        return $this->buildItem(
            item: $item,
            fidelity: $fidelity,
            structuredAttributes: $this->baseAttributes($item, $meta),
            gapSummary: GapSummary::fromItemMeta($meta, $fidelity, usesFallback: true),
        );
    }

    /**
     * @param  array<string, mixed>  $meta
     * @param  array<int, RenderedAttribute>  $structuredAttributes
     */
    protected function buildItem(
        BaselineSnapshotItem $item,
        FidelityState $fidelity,
        array $structuredAttributes,
        GapSummary $gapSummary,
        array $meta = [],
    ): RenderedSnapshotItem {
        $meta = $meta === [] ? $this->meta($item) : $meta;

        return new RenderedSnapshotItem(
            label: $this->displayLabel($item, $meta),
            typeLabel: $this->typeLabel((string) $item->policy_type),
            identityHint: $this->identityHint($item, $meta),
            referenceStatus: $this->referenceStatus($meta),
            fidelity: $fidelity,
            gapSummary: $gapSummary,
            observedAt: $this->observedAt($meta),
            sourceReference: $this->sourceReference($meta),
            structuredAttributes: $structuredAttributes,
        );
    }

    /**
     * @param  array<string, mixed>  $meta
     * @return array<int, RenderedAttribute>
     */
    protected function baseAttributes(BaselineSnapshotItem $item, array $meta): array
    {
        $attributes = [];

        $category = $meta['category'] ?? InventoryPolicyTypeMeta::category((string) $item->policy_type);
        $platform = $meta['platform'] ?? null;
        $source = data_get($meta, 'evidence.source');
        $identityStrategy = data_get($meta, 'identity.strategy');
        $policyVersionId = data_get($meta, 'version_reference.policy_version_id');
        $capturePurpose = data_get($meta, 'version_reference.capture_purpose');

        if (is_string($category) && trim($category) !== '') {
            $attributes[] = new RenderedAttribute('Category', trim($category));
        }

        if (is_string($platform) && trim($platform) !== '') {
            $attributes[] = new RenderedAttribute('Platform', $this->humanizeValue($platform));
        }

        if (is_string($source) && trim($source) !== '') {
            $attributes[] = new RenderedAttribute('Evidence source', $this->referenceStatus($meta));
        }

        if (is_string($identityStrategy) && trim($identityStrategy) !== '') {
            $attributes[] = new RenderedAttribute('Identity strategy', $this->humanizeValue($identityStrategy), 'secondary');
        }

        if (is_numeric($policyVersionId)) {
            $attributes[] = new RenderedAttribute('Policy version', '#'.((int) $policyVersionId), 'secondary');
        }

        if (is_string($capturePurpose) && trim($capturePurpose) !== '') {
            $attributes[] = new RenderedAttribute('Capture purpose', $this->humanizeValue($capturePurpose), 'secondary');
        }

        return $attributes;
    }

    /**
     * @return array<string, mixed>
     */
    protected function meta(BaselineSnapshotItem $item): array
    {
        return is_array($item->meta_jsonb) ? $item->meta_jsonb : [];
    }

    /**
     * @param  array<string, mixed>  $meta
     */
    protected function displayLabel(BaselineSnapshotItem $item, array $meta): string
    {
        $displayName = $meta['display_name'] ?? null;

        if (is_string($displayName) && trim($displayName) !== '') {
            return trim($displayName);
        }

        if (is_string($item->subject_key) && trim($item->subject_key) !== '') {
            return Str::headline($item->subject_key);
        }

        return $this->typeLabel((string) $item->policy_type);
    }

    protected function typeLabel(string $policyType): string
    {
        return InventoryPolicyTypeMeta::baselineCompareLabel($policyType)
            ?? InventoryPolicyTypeMeta::label($policyType)
            ?? Str::headline($policyType);
    }

    /**
     * @param  array<string, mixed>  $meta
     */
    protected function identityHint(BaselineSnapshotItem $item, array $meta): string
    {
        $identityStrategy = data_get($meta, 'identity.strategy');

        if ($identityStrategy === 'external_id' && is_string($item->subject_key) && $item->subject_key !== '') {
            return 'External ID hash '.Str::limit($item->subject_key, 12, '…');
        }

        if (is_string($item->subject_key) && trim($item->subject_key) !== '') {
            return $item->subject_key;
        }

        if (is_string($item->subject_external_id) && trim($item->subject_external_id) !== '') {
            return 'Snapshot key '.Str::limit($item->subject_external_id, 12, '…');
        }

        return '—';
    }

    /**
     * @param  array<string, mixed>  $meta
     */
    protected function referenceStatus(array $meta): string
    {
        $source = Str::of((string) data_get($meta, 'evidence.source', 'captured'))
            ->trim()
            ->lower()
            ->replace(['-', ' '], '_')
            ->toString();

        return match ($source) {
            'policy_version' => 'Policy version',
            'inventory', 'metadata_only' => 'Inventory metadata',
            default => 'Captured metadata',
        };
    }

    /**
     * @param  array<string, mixed>  $meta
     */
    protected function observedAt(array $meta): ?string
    {
        $observedAt = data_get($meta, 'evidence.observed_at');

        return is_string($observedAt) && trim($observedAt) !== ''
            ? trim($observedAt)
            : null;
    }

    /**
     * @param  array<string, mixed>  $meta
     */
    protected function sourceReference(array $meta): ?string
    {
        $policyVersionId = data_get($meta, 'version_reference.policy_version_id');
        $capturePurpose = data_get($meta, 'version_reference.capture_purpose');

        $parts = [];

        if (is_numeric($policyVersionId)) {
            $parts[] = 'Policy version #'.((int) $policyVersionId);
        }

        if (is_string($capturePurpose) && trim($capturePurpose) !== '') {
            $parts[] = $this->humanizeValue($capturePurpose);
        }

        if ($parts === []) {
            return null;
        }

        return implode(' · ', $parts);
    }

    protected function humanizeValue(mixed $value): string
    {
        return Str::of((string) $value)
            ->replace(['_', '-'], ' ')
            ->headline()
            ->toString();
    }
}
