<?php

declare(strict_types=1);

namespace App\Domain\PreSave;

use App\Domain\PreSave\ValueObjects\RegistrationStatus;
use App\Domain\PreSave\ValueObjects\StreamingPlatform;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\ValueObjects\SqlQuery;

/**
 * Pre-Save Registration Repository
 */
final readonly class PreSaveRegistrationRepository implements PreSaveRegistrationRepositoryInterface
{
    private const TABLE = 'presave_registrations';

    public function __construct(
        private ConnectionInterface $connection,
    ) {
    }

    /**
     * Find registration by ID
     */
    public function findById(int $id): ?PreSaveRegistration
    {
        $sql = "SELECT * FROM " . self::TABLE . " WHERE id = ? LIMIT 1";
        $query = SqlQuery::create($sql, [$id]);
        $result = $this->connection->query($query);
        $row = $result->fetchOne();

        return $row ? PreSaveRegistration::fromArray($row) : null;
    }

    /**
     * Find registration for user and campaign
     */
    public function findForUserAndCampaign(
        string $userId,
        int $campaignId,
        StreamingPlatform $platform
    ): ?PreSaveRegistration {
        $sql = "SELECT * FROM " . self::TABLE . "
                WHERE user_id = ? AND campaign_id = ? AND platform = ?
                LIMIT 1";

        $query = SqlQuery::create($sql, [$userId, $campaignId, $platform->value]);
        $result = $this->connection->query($query);
        $row = $result->fetchOne();

        return $row ? PreSaveRegistration::fromArray($row) : null;
    }

    /**
     * Find all registrations for user
     *
     * @return array<PreSaveRegistration>
     */
    public function findByUserId(string $userId): array
    {
        $query = SqlQuery::select(table: self::TABLE)
            ->where('user_id = ?', [$userId])
            ->orderBy('registered_at DESC');

        $result = $this->connection->query($query);
        $rows = $result->fetchAll();

        return array_map(
            fn (array $row) => PreSaveRegistration::fromArray($row),
            $rows
        );
    }

    /**
     * Find all registrations for campaign
     *
     * @return array<PreSaveRegistration>
     */
    public function findByCampaign(int $campaignId): array
    {
        $query = SqlQuery::select(self::TABLE)
            ->where('campaign_id = ?', [$campaignId])
            ->orderBy('registered_at DESC');

        $result = $this->connection->query($query);
        $rows = $result->fetchAll();

        return array_map(
            fn (array $row) => PreSaveRegistration::fromArray($row),
            $rows
        );
    }

    /**
     * Find pending registrations for campaign
     *
     * @return array<PreSaveRegistration>
     */
    public function findPendingByCampaign(int $campaignId): array
    {
        $sql = "SELECT * FROM " . self::TABLE . "
                WHERE campaign_id = ? AND status = ?
                ORDER BY registered_at ASC";

        $query = SqlQuery::create($sql, [
            $campaignId,
            RegistrationStatus::PENDING->value,
        ]);
        $result = $this->connection->query($query);
        $rows = $result->fetchAll();

        return array_map(
            fn (array $row) => PreSaveRegistration::fromArray($row),
            $rows
        );
    }

    /**
     * Find failed registrations that can be retried
     *
     * @return array<PreSaveRegistration>
     */
    public function findRetryable(int $campaignId, int $maxRetries = 3): array
    {
        $sql = "SELECT * FROM " . self::TABLE . "
                WHERE campaign_id = ?
                AND status = ?
                AND retry_count < ?
                ORDER BY registered_at ASC";

        $query = SqlQuery::create($sql, [
            $campaignId,
            RegistrationStatus::FAILED->value,
            $maxRetries,
        ]);
        $result = $this->connection->query($query);
        $rows = $result->fetchAll();

        return array_map(
            fn (array $row) => PreSaveRegistration::fromArray($row),
            $rows
        );
    }

    /**
     * Check if user already registered for campaign
     */
    public function hasRegistered(
        string $userId,
        int $campaignId,
        StreamingPlatform $platform
    ): bool {
        return $this->findForUserAndCampaign($userId, $campaignId, $platform) !== null;
    }

    /**
     * Save or update registration
     */
    public function save(PreSaveRegistration $registration): PreSaveRegistration
    {
        if ($registration->id === null) {
            return $this->insert($registration);
        }

        return $this->update($registration);
    }

    /**
     * Delete registration
     */
    public function delete(int $id): bool
    {
        $sql = "DELETE FROM " . self::TABLE . " WHERE id = ?";
        $query = SqlQuery::create($sql, [$id]);
        $this->connection->execute($query);

        return true;
    }

    /**
     * Delete all registrations for campaign
     */
    public function deleteByCampaign(int $campaignId): int
    {
        $sql = "DELETE FROM " . self::TABLE . " WHERE campaign_id = ?";
        $query = SqlQuery::create($sql, [$campaignId]);
        $this->connection->execute($query);

        return $this->connection->rowCount();
    }

    /**
     * Get registration count by status
     *
     * @return array<string, int>
     */
    public function getStatusCounts(int $campaignId): array
    {
        $sql = "SELECT status, COUNT(*) as count
                FROM " . self::TABLE . "
                WHERE campaign_id = ?
                GROUP BY status";

        $query = SqlQuery::create($sql, [$campaignId]);
        $result = $this->connection->query($query);
        $rows = $result->fetchAll();

        $counts = [];
        foreach ($rows as $row) {
            $counts[$row['status']] = (int) $row['count'];
        }

        return $counts;
    }

    /**
     * Insert new registration
     */
    private function insert(PreSaveRegistration $registration): PreSaveRegistration
    {
        $data = $registration->toArray();

        $sql = "INSERT INTO " . self::TABLE . "
                (campaign_id, user_id, platform, status, registered_at, processed_at, error_message, retry_count)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?)";

        $query = SqlQuery::create($sql, [
            $data['campaign_id'],
            $data['user_id'],
            $data['platform'],
            $data['status'],
            $data['registered_at'],
            $data['processed_at'],
            $data['error_message'],
            $data['retry_count'],
        ]);

        $this->connection->execute($query);

        $id = (int) $this->connection->lastInsertId();

        return new PreSaveRegistration(
            id: $id,
            campaignId: $registration->campaignId,
            userId: $registration->userId,
            platform: $registration->platform,
            status: $registration->status,
            registeredAt: $registration->registeredAt,
            processedAt: $registration->processedAt,
            errorMessage: $registration->errorMessage,
            retryCount: $registration->retryCount,
        );
    }

    /**
     * Update existing registration
     */
    private function update(PreSaveRegistration $registration): PreSaveRegistration
    {
        $data = $registration->toArray();

        $sql = "UPDATE " . self::TABLE . "
                SET status = ?,
                    processed_at = ?,
                    error_message = ?,
                    retry_count = ?
                WHERE id = ?";

        $query = SqlQuery::create($sql, [
            $data['status'],
            $data['processed_at'],
            $data['error_message'],
            $data['retry_count'],
            $registration->id,
        ]);

        $this->connection->execute($query);

        return $registration;
    }
}
