<?php

declare(strict_types=1);

namespace App\Filament\Support;

final class NormalizedDiffSurface
{
    /**
     * @param  array<string, mixed>  $diff
     * @return array<string, mixed>
     */
    public static function build(array $diff, string $hostKind): array
    {
        $summary = is_array($diff['summary'] ?? null) ? $diff['summary'] : [];
        $added = is_array($diff['added'] ?? null) ? $diff['added'] : [];
        $removed = is_array($diff['removed'] ?? null) ? $diff['removed'] : [];
        $changed = is_array($diff['changed'] ?? null) ? $diff['changed'] : [];
        $message = is_string($summary['message'] ?? null) && trim((string) $summary['message']) !== ''
            ? trim((string) $summary['message'])
            : null;

        $addedCount = is_numeric($summary['added'] ?? null) ? (int) $summary['added'] : count($added);
        $removedCount = is_numeric($summary['removed'] ?? null) ? (int) $summary['removed'] : count($removed);
        $changedCount = is_numeric($summary['changed'] ?? null) ? (int) $summary['changed'] : count($changed);
        $availabilityState = self::availabilityState($message, $addedCount, $removedCount, $changedCount);

        return [
            'hostKind' => $hostKind,
            'availabilityState' => $availabilityState,
            'summary' => [
                'added' => $addedCount,
                'removed' => $removedCount,
                'changed' => $changedCount,
                'message' => $message,
            ],
            'viewModes' => [
                ['key' => 'grouped', 'label' => 'Grouped diff', 'default' => true],
            ],
            'sectionBehavior' => [
                'preservesGroupOrder' => true,
                'supportsExpansion' => true,
                'supportsFullscreen' => true,
            ],
            'renderExpectations' => [
                'ownsAvailabilityState' => true,
                'ownsZeroDiffMessaging' => true,
                'keepsHostFramingOutsideCore' => true,
            ],
            'groups' => [
                self::group('changed', 'Changed', $changed, false),
                self::group('added', 'Added', $added, true),
                self::group('removed', 'Removed', $removed, true),
            ],
            'scriptRendering' => [
                'policyType' => $diff['policy_type'] ?? null,
                'showScriptContent' => (bool) config('tenantpilot.display.show_script_content', false),
            ],
            'emptyState' => self::emptyState($availabilityState, $message, $addedCount, $removedCount, $changedCount),
            'raw' => [
                'added' => $added,
                'removed' => $removed,
                'changed' => $changed,
            ],
        ];
    }

    /**
     * @param  array<string, mixed>  $items
     * @return array<string, mixed>
     */
    private static function group(string $key, string $label, array $items, bool $collapsed): array
    {
        return [
            'key' => $key,
            'label' => $label,
            'collapsed' => $collapsed,
            'count' => count($items),
            'items' => self::groupByBlock($items),
        ];
    }

    /**
     * @param  array<string, mixed>  $items
     * @return array<string, array<string, mixed>>
     */
    private static function groupByBlock(array $items): array
    {
        $groups = [];

        foreach ($items as $path => $value) {
            if (! is_string($path) || $path === '') {
                continue;
            }

            $parts = explode(' > ', $path, 2);
            $group = count($parts) === 2 ? $parts[0] : 'Other';
            $label = count($parts) === 2 ? $parts[1] : $path;

            $groups[$group][$label] = $value;
        }

        ksort($groups);

        return $groups;
    }

    private static function availabilityState(?string $message, int $addedCount, int $removedCount, int $changedCount): string
    {
        if ($message !== null && str_contains(strtolower($message), 'unavailable')) {
            return 'unavailable';
        }

        if ($message !== null && str_contains(strtolower($message), 'partial')) {
            return 'partial';
        }

        return 'available';
    }

    /**
     * @return array{title: string, message: string}|null
     */
    private static function emptyState(
        string $availabilityState,
        ?string $message,
        int $addedCount,
        int $removedCount,
        int $changedCount,
    ): ?array
    {
        if ($availabilityState === 'unavailable' && $message !== null) {
            return [
                'title' => 'Diff unavailable',
                'message' => $message,
            ];
        }

        if ($availabilityState === 'partial' && $message !== null) {
            return [
                'title' => 'Diff partially available',
                'message' => $message,
            ];
        }

        if ($availabilityState === 'available' && ($addedCount + $removedCount + $changedCount) === 0) {
            return [
                'title' => 'No normalized changes',
                'message' => $message ?? 'No normalized changes were found.',
            ];
        }

        return null;
    }
}
