<?php

declare(strict_types=1);

namespace App\Domain\SmartLink\Repositories;

use App\Domain\SmartLink\Entities\GeoRoutingRule;
use App\Domain\SmartLink\ValueObjects\GeoRuleId;
use App\Domain\SmartLink\ValueObjects\SmartLinkId;
use App\Framework\Core\ValueObjects\CountryCode;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\ValueObjects\SqlQuery;

final readonly class DatabaseGeoRoutingRuleRepository implements GeoRoutingRuleRepository
{
    public function __construct(
        private ConnectionInterface $connection
    ) {
    }

    public function save(GeoRoutingRule $rule): void
    {
        $sql = 'INSERT INTO geo_routing_rules
                (id, link_id, country_code, destination_id, priority, created_at)
                VALUES (?, ?, ?, ?, ?, ?)
                ON DUPLICATE KEY UPDATE
                    country_code = VALUES(country_code),
                    destination_id = VALUES(destination_id),
                    priority = VALUES(priority)';

        $query = SqlQuery::create($sql, [
            $rule->id->toString(),
            $rule->linkId->toString(),
            $rule->countryCode->toString(),
            $rule->destinationId,
            $rule->priority,
            $rule->createdAt->format('Y-m-d H:i:s'),
        ]);

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

    public function findById(GeoRuleId $id): ?GeoRoutingRule
    {
        $sql = 'SELECT * FROM geo_routing_rules WHERE id = ?';
        $query = SqlQuery::create($sql, [$id->toString()]);

        $result = $this->connection->query($query);
        $row = $result->fetch();

        if ($row === false) {
            return null;
        }

        return $this->hydrate($row);
    }

    public function findByLinkAndCountry(SmartLinkId $linkId, CountryCode $countryCode): ?GeoRoutingRule
    {
        $sql = 'SELECT * FROM geo_routing_rules
                WHERE link_id = ? AND country_code = ?
                ORDER BY priority DESC
                LIMIT 1';

        $query = SqlQuery::create($sql, [
            $linkId->toString(),
            $countryCode->toString(),
        ]);

        $result = $this->connection->query($query);
        $row = $result->fetch();

        if ($row === false) {
            return null;
        }

        return $this->hydrate($row);
    }

    public function findByLinkId(SmartLinkId $linkId): array
    {
        $sql = 'SELECT * FROM geo_routing_rules
                WHERE link_id = ?
                ORDER BY priority DESC, country_code ASC';

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

        return array_map(fn (array $row) => $this->hydrate($row), $rows);
    }

    public function delete(GeoRuleId $id): void
    {
        $sql = 'DELETE FROM geo_routing_rules WHERE id = ?';
        $query = SqlQuery::create($sql, [$id->toString()]);

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

    public function deleteByLinkId(SmartLinkId $linkId): void
    {
        $sql = 'DELETE FROM geo_routing_rules WHERE link_id = ?';
        $query = SqlQuery::create($sql, [$linkId->toString()]);

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

    private function hydrate(array $row): GeoRoutingRule
    {
        return new GeoRoutingRule(
            id: GeoRuleId::fromString($row['id']),
            linkId: SmartLinkId::fromString($row['link_id']),
            countryCode: CountryCode::fromString($row['country_code']),
            destinationId: $row['destination_id'],
            priority: (int) $row['priority'],
            createdAt: Timestamp::fromDateTime(new \DateTimeImmutable($row['created_at']))
        );
    }
}
