<?php

declare(strict_types=1);

namespace App\Domain\Meta\Repository;

use App\Domain\Meta\Entity\MetaEntry;
use App\Domain\Meta\ValueObject\MetaData;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\ValueObjects\SqlQuery;

final readonly class DatabaseMetaRepository implements MetaRepositoryInterface
{
    public function __construct(
        private readonly ConnectionInterface $connection
    ) {
    }

    public function findByRoute(string $route): ?MetaEntry
    {
        $entries = $this->findAllByRoute($route);

        return $entries[0] ?? null;
    }

    public function findByEntity(string $entityType, int $entityId): ?MetaEntry
    {
        $sql = '
            SELECT id, route_pattern, entity_type, entity_id, meta_data, priority, active, created_at, updated_at
            FROM meta_entries
            WHERE entity_type = ? AND entity_id = ? AND active = 1
            ORDER BY priority DESC
            LIMIT 1
        ';

        $row = $this->connection->queryOne(SqlQuery::create($sql, [$entityType, $entityId]));

        if ($row) {
            return $this->hydrateFromRow($row);
        }

        return null;
    }

    public function findAllByRoute(string $route): array
    {
        $sql = '
            SELECT id, route_pattern, entity_type, entity_id, meta_data, priority, active, created_at, updated_at
            FROM meta_entries
            WHERE route_pattern IS NOT NULL
            AND active = 1
            ORDER BY priority DESC, LENGTH(route_pattern) DESC
        ';

        $result = $this->connection->query(SqlQuery::create($sql));
        $entries = [];

        foreach ($result->fetchAll() as $row) {
            $entry = $this->hydrateFromRow($row);
            if ($entry->matchesRoute($route)) {
                $entries[] = $entry;
            }
        }

        return $entries;
    }

    public function save(MetaEntry $entry): MetaEntry
    {
        if ($entry->id === null) {
            return $this->insert($entry);
        } else {
            return $this->update($entry);
        }
    }

    private function insert(MetaEntry $entry): MetaEntry
    {
        $sql = '
            INSERT INTO meta_entries (
                route_pattern, entity_type, entity_id,
                title, description, keywords, og_title, og_description,
                og_image, og_type, twitter_card, twitter_site,
                canonical, custom_meta, priority, active, created_at, updated_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ';

        $metaArray = $entry->metaData->toArray();

        $this->connection->execute(SqlQuery::create($sql, [
            $entry->routePattern,
            $entry->entityType,
            $entry->entityId,
            $metaArray['title'] ?? null,
            $metaArray['description'] ?? null,
            $metaArray['keywords'] ?? null,
            $metaArray['og_title'] ?? null,
            $metaArray['og_description'] ?? null,
            $metaArray['og_image'] ?? null,
            $metaArray['og_type'] ?? null,
            $metaArray['twitter_card'] ?? null,
            $metaArray['twitter_site'] ?? null,
            $metaArray['canonical'] ?? null,
            $metaArray['custom_meta'] ?? null,
            $entry->priority,
            $entry->active ? 1 : 0,
            $entry->createdAt->format('Y-m-d H:i:s'),
            $entry->updatedAt->format('Y-m-d H:i:s'),
        ]));

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

        return $entry;
    }

    private function update(MetaEntry $entry): MetaEntry
    {
        $sql = '
            UPDATE meta_entries SET
                route_pattern = ?, entity_type = ?, entity_id = ?,
                title = ?, description = ?, keywords = ?, og_title = ?,
                og_description = ?, og_image = ?, og_type = ?, twitter_card = ?,
                twitter_site = ?, canonical = ?, custom_meta = ?,
                priority = ?, active = ?, updated_at = ?
            WHERE id = ?
        ';

        $metaArray = $entry->metaData->toArray();
        $entry->updatedAt = new \DateTimeImmutable();

        $this->connection->execute(SqlQuery::create($sql, [
            $entry->routePattern,
            $entry->entityType,
            $entry->entityId,
            $metaArray['title'] ?? null,
            $metaArray['description'] ?? null,
            $metaArray['keywords'] ?? null,
            $metaArray['og_title'] ?? null,
            $metaArray['og_description'] ?? null,
            $metaArray['og_image'] ?? null,
            $metaArray['og_type'] ?? null,
            $metaArray['twitter_card'] ?? null,
            $metaArray['twitter_site'] ?? null,
            $metaArray['canonical'] ?? null,
            $metaArray['custom_meta'] ?? null,
            $entry->priority,
            $entry->active ? 1 : 0,
            $entry->updatedAt->format('Y-m-d H:i:s'),
            $entry->id,
        ]));

        return $entry;
    }

    public function delete(int $id): bool
    {
        $sql = 'DELETE FROM meta_entries WHERE id = ?';
        $affectedRows = $this->connection->execute(SqlQuery::create($sql, [$id]));

        return $affectedRows > 0;
    }

    public function findById(int $id): ?MetaEntry
    {
        $sql = '
            SELECT id, route_pattern, entity_type, entity_id, meta_data, priority, active, created_at, updated_at
            FROM meta_entries WHERE id = ?
        ';

        $row = $this->connection->queryOne(SqlQuery::create($sql, [$id]));

        if ($row) {
            return $this->hydrateFromRow($row);
        }

        return null;
    }

    public function findAll(int $limit = 50, int $offset = 0): array
    {
        $sql = '
            SELECT id, route_pattern, entity_type, entity_id, meta_data, priority, active, created_at, updated_at
            FROM meta_entries
            ORDER BY created_at DESC
            LIMIT ? OFFSET ?
        ';

        $result = $this->connection->query(SqlQuery::create($sql, [$limit, $offset]));
        $entries = [];

        foreach ($result->fetchAll() as $row) {
            $entries[] = $this->hydrateFromRow($row);
        }

        return $entries;
    }

    public function search(string $query, int $limit = 50): array
    {
        $sql = '
            SELECT id, route_pattern, entity_type, entity_id, meta_data, priority, active, created_at, updated_at
            FROM meta_entries
            WHERE route_pattern LIKE ?
            OR title LIKE ?
            OR description LIKE ?
            ORDER BY created_at DESC
            LIMIT ?
        ';

        $searchTerm = "%{$query}%";
        $result = $this->connection->query(SqlQuery::create($sql, [$searchTerm, $searchTerm, $searchTerm, $limit]));
        $entries = [];

        foreach ($result->fetchAll() as $row) {
            $entries[] = $this->hydrateFromRow($row);
        }

        return $entries;
    }

    public function count(): int
    {
        $sql = 'SELECT COUNT(*) FROM meta_entries';
        $result = $this->connection->query(SqlQuery::create($sql));

        return (int) $result->fetchColumn();
    }

    public function routePatternExists(string $routePattern, ?int $excludeId = null): bool
    {
        $sql = 'SELECT COUNT(*) FROM meta_entries WHERE route_pattern = ?';
        $params = [$routePattern];

        if ($excludeId !== null) {
            $sql .= ' AND id != ?';
            $params[] = $excludeId;
        }

        $result = $this->connection->query(SqlQuery::create($sql, $params));

        return (int) $result->fetchColumn() > 0;
    }

    public function entityMetaExists(string $entityType, int $entityId, ?int $excludeId = null): bool
    {
        $sql = 'SELECT COUNT(*) FROM meta_entries WHERE entity_type = ? AND entity_id = ?';
        $params = [$entityType, $entityId];

        if ($excludeId !== null) {
            $sql .= ' AND id != ?';
            $params[] = $excludeId;
        }

        $result = $this->connection->query(SqlQuery::create($sql, $params));

        return (int) $result->fetchColumn() > 0;
    }

    private function hydrateFromRow(array $row): MetaEntry
    {
        $metaData = MetaData::fromArray([
            'title' => $row['title'],
            'description' => $row['description'],
            'keywords' => $row['keywords'],
            'og_title' => $row['og_title'],
            'og_description' => $row['og_description'],
            'og_image' => $row['og_image'],
            'og_type' => $row['og_type'],
            'twitter_card' => $row['twitter_card'],
            'twitter_site' => $row['twitter_site'],
            'canonical' => $row['canonical'],
            'custom_meta' => $row['custom_meta'],
        ]);

        return new MetaEntry(
            id: (int) $row['id'],
            routePattern: $row['route_pattern'],
            entityType: $row['entity_type'],
            entityId: $row['entity_id'] ? (int) $row['entity_id'] : null,
            metaData: $metaData,
            priority: (int) $row['priority'],
            active: (bool) $row['active'],
            createdAt: new \DateTimeImmutable($row['created_at']),
            updatedAt: new \DateTimeImmutable($row['updated_at']),
        );
    }
}
