<?php

declare(strict_types=1);

namespace App\Infrastructure\Reporter;

use App\Domain\Reporter\Reporter;
use DateTimeImmutable;
use DateTimeZone;
use Doctrine\DBAL\Connection;

/**
 * DBAL gateway for the reporters table.
 *
 * `softDelete()` flips `is_active=false`; the SPEC's "hard delete refused
 * if reports exist (409)" rule lives in the controller — the repo just
 * exposes the count so the controller can decide.
 */
final class ReporterRepository
{
    public function __construct(private readonly Connection $connection)
    {
    }

    public function findById(int $id): ?Reporter
    {
        /** @var array<string, mixed>|false $row */
        $row = $this->connection->fetchAssociative(
            'SELECT id, name, description, trust_weight, is_active, audit_enabled, created_by_user_id, created_at '
            . 'FROM reporters WHERE id = :id',
            ['id' => $id]
        );

        return $row === false ? null : self::hydrate($row);
    }

    public function findByName(string $name): ?Reporter
    {
        /** @var array<string, mixed>|false $row */
        $row = $this->connection->fetchAssociative(
            'SELECT id, name, description, trust_weight, is_active, audit_enabled, created_by_user_id, created_at '
            . 'FROM reporters WHERE name = :name',
            ['name' => $name]
        );

        return $row === false ? null : self::hydrate($row);
    }

    /**
     * @return list<Reporter>
     */
    public function list(int $limit, int $offset): array
    {
        /** @var list<array<string, mixed>> $rows */
        $rows = $this->connection->fetchAllAssociative(
            'SELECT id, name, description, trust_weight, is_active, audit_enabled, created_by_user_id, created_at '
            . 'FROM reporters ORDER BY id DESC LIMIT :limit OFFSET :offset',
            ['limit' => $limit, 'offset' => $offset]
        );

        return array_map(static fn (array $r): Reporter => self::hydrate($r), $rows);
    }

    public function count(): int
    {
        return (int) $this->connection->fetchOne('SELECT COUNT(*) FROM reporters');
    }

    public function create(string $name, ?string $description, float $trustWeight, ?int $createdByUserId): int
    {
        $this->connection->insert('reporters', [
            'name' => $name,
            'description' => $description,
            'trust_weight' => number_format($trustWeight, 2, '.', ''),
            'is_active' => 1,
            'audit_enabled' => 1,
            'created_by_user_id' => $createdByUserId,
        ]);

        return (int) $this->connection->lastInsertId();
    }

    /**
     * @param array<string, mixed> $fields
     */
    public function update(int $id, array $fields): void
    {
        if ($fields === []) {
            return;
        }
        $this->connection->update('reporters', $fields, ['id' => $id]);
    }

    public function softDelete(int $id): void
    {
        $this->connection->update('reporters', ['is_active' => 0], ['id' => $id]);
    }

    public function reportCount(int $reporterId): int
    {
        return (int) $this->connection->fetchOne(
            'SELECT COUNT(*) FROM reports WHERE reporter_id = :id',
            ['id' => $reporterId]
        );
    }

    /**
     * @param array<string, mixed> $row
     */
    private static function hydrate(array $row): Reporter
    {
        $createdAt = isset($row['created_at']) && $row['created_at'] !== null
            ? new DateTimeImmutable((string) $row['created_at'], new DateTimeZone('UTC'))
            : new DateTimeImmutable('now', new DateTimeZone('UTC'));

        return new Reporter(
            id: (int) $row['id'],
            name: (string) $row['name'],
            description: $row['description'] !== null ? (string) $row['description'] : null,
            trustWeight: (float) $row['trust_weight'],
            isActive: (bool) $row['is_active'],
            auditEnabled: (bool) ($row['audit_enabled'] ?? true),
            createdByUserId: $row['created_by_user_id'] !== null ? (int) $row['created_by_user_id'] : null,
            createdAt: $createdAt,
        );
    }
}
