<?php

declare(strict_types=1);

namespace App\ApiClient\DTOs;

/**
 * Per-IP detail payload from `GET /api/v1/admin/ips/{ip}`. Carries
 * everything the detail page renders: scores per category, enrichment
 * (null until M11), manual/allowlist panels, history timeline.
 */
final class IpDetailDto
{
    /**
     * @param list<array<string, mixed>> $scores entries shaped {category, category_id, score, last_report_at, report_count_30d}
     * @param array{country_code: ?string, asn: ?int, as_org: ?string, enriched_at: ?string} $enrichment
     * @param array<string, mixed>|null $manualBlock
     * @param array<string, mixed>|null $allowlist
     * @param list<array<string, mixed>> $history
     */
    public function __construct(
        public readonly string $ip,
        public readonly bool $isIpv4,
        public readonly array $scores,
        public readonly array $enrichment,
        public readonly string $status,
        public readonly ?array $manualBlock,
        public readonly ?array $allowlist,
        public readonly array $history,
        public readonly bool $hasMore,
    ) {
    }

    /**
     * @param array<string, mixed> $payload
     */
    public static function fromArray(array $payload): self
    {
        /** @var list<array<string, mixed>> $scores */
        $scores = [];
        if (isset($payload['scores']) && is_array($payload['scores'])) {
            foreach ($payload['scores'] as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $scores[] = [
                    'category' => isset($row['category']) && $row['category'] !== null ? (string) $row['category'] : null,
                    'category_id' => (int) ($row['category_id'] ?? 0),
                    'score' => (float) ($row['score'] ?? 0),
                    'last_report_at' => isset($row['last_report_at']) && $row['last_report_at'] !== null ? (string) $row['last_report_at'] : null,
                    'report_count_30d' => (int) ($row['report_count_30d'] ?? 0),
                ];
            }
        }

        $enrichment = [
            'country_code' => null,
            'asn' => null,
            'as_org' => null,
            'enriched_at' => null,
        ];
        if (isset($payload['enrichment']) && is_array($payload['enrichment'])) {
            foreach (['country_code', 'as_org', 'enriched_at'] as $key) {
                if (isset($payload['enrichment'][$key]) && $payload['enrichment'][$key] !== null) {
                    $enrichment[$key] = (string) $payload['enrichment'][$key];
                }
            }
            if (isset($payload['enrichment']['asn']) && $payload['enrichment']['asn'] !== null) {
                $enrichment['asn'] = (int) $payload['enrichment']['asn'];
            }
        }

        /** @var list<array<string, mixed>> $history */
        $history = [];
        if (isset($payload['history']) && is_array($payload['history'])) {
            foreach ($payload['history'] as $row) {
                if (is_array($row)) {
                    $history[] = $row;
                }
            }
        }

        return new self(
            ip: (string) ($payload['ip'] ?? ''),
            isIpv4: (bool) ($payload['is_ipv4'] ?? false),
            scores: $scores,
            enrichment: $enrichment,
            status: (string) ($payload['status'] ?? 'clean'),
            manualBlock: isset($payload['manual_block']) && is_array($payload['manual_block']) ? $payload['manual_block'] : null,
            allowlist: isset($payload['allowlist']) && is_array($payload['allowlist']) ? $payload['allowlist'] : null,
            history: $history,
            hasMore: (bool) ($payload['has_more'] ?? false),
        );
    }

    public function maxScore(): float
    {
        $max = 0.0;
        foreach ($this->scores as $row) {
            if ($row['score'] > $max) {
                $max = $row['score'];
            }
        }

        return $max;
    }
}
