<?php

declare(strict_types=1);

namespace App\Domain\PreSave;

use App\Domain\PreSave\ValueObjects\CampaignStatus;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\ValueObjects\SqlQuery;

/**
 * Pre-Save Campaign Repository
 */
final readonly class PreSaveCampaignRepository implements PreSaveCampaignRepositoryInterface
{
    private const TABLE = 'presave_campaigns';

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

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

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

    /**
     * Find all campaigns
     *
     * @param array<string, mixed> $filters
     * @return array<PreSaveCampaign>
     */
    public function findAll(array $filters = []): array
    {
        $sql = "SELECT * FROM " . self::TABLE;
        $params = [];

        if (isset($filters['status'])) {
            $sql .= " WHERE status = ?";
            $params[] = $filters['status'];
        }

        $sql .= " ORDER BY release_date DESC";

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

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

    /**
     * Find campaigns that should be released (release date passed, status = scheduled/active)
     *
     * @return array<PreSaveCampaign>
     */
    public function findReadyForRelease(): array
    {
        $now = Timestamp::now()->toTimestamp();

        $sql = "SELECT * FROM " . self::TABLE . "
                WHERE release_date <= ?
                AND status IN (?, ?)
                ORDER BY release_date ASC";

        $query = SqlQuery::create($sql, [
            $now,
            CampaignStatus::SCHEDULED->value,
            CampaignStatus::ACTIVE->value,
        ]);
        $result = $this->connection->query($query);
        $rows = $result->fetchAll();

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

    /**
     * Find campaigns ready for processing (status = released)
     *
     * @return array<PreSaveCampaign>
     */
    public function findReadyForProcessing(): array
    {
        $sql = "SELECT * FROM " . self::TABLE . "
                WHERE status = ?
                ORDER BY release_date ASC";

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

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

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

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

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

        return true;
    }

    /**
     * Get campaign statistics
     *
     * @return array<string, int>
     */
    public function getStatistics(int $campaignId): array
    {
        $sql = "SELECT
                    COUNT(*) as total_registrations,
                    SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
                    SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,
                    SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
                FROM presave_registrations
                WHERE campaign_id = ?";

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

        return [
            'total_registrations' => (int) ($row['total_registrations'] ?? 0),
            'completed' => (int) ($row['completed'] ?? 0),
            'pending' => (int) ($row['pending'] ?? 0),
            'failed' => (int) ($row['failed'] ?? 0),
        ];
    }

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

        $sql = "INSERT INTO " . self::TABLE . "
                (title, artist_name, cover_image_url, description, release_date, start_date, track_urls, status, created_at, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

        $query = SqlQuery::create($sql, [
            $data['title'],
            $data['artist_name'],
            $data['cover_image_url'],
            $data['description'],
            $data['release_date'],
            $data['start_date'],
            json_encode($data['track_urls']),
            $data['status'],
            $data['created_at'],
            $data['updated_at'],
        ]);

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

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

        return new PreSaveCampaign(
            id: $id,
            title: $campaign->title,
            artistName: $campaign->artistName,
            coverImageUrl: $campaign->coverImageUrl,
            releaseDate: $campaign->releaseDate,
            trackUrls: $campaign->trackUrls,
            status: $campaign->status,
            createdAt: $campaign->createdAt,
            updatedAt: $campaign->updatedAt,
            description: $campaign->description,
            startDate: $campaign->startDate,
        );
    }

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

        $sql = "UPDATE " . self::TABLE . "
                SET title = ?,
                    artist_name = ?,
                    cover_image_url = ?,
                    description = ?,
                    release_date = ?,
                    start_date = ?,
                    track_urls = ?,
                    status = ?,
                    updated_at = ?
                WHERE id = ?";

        $query = SqlQuery::create($sql, [
            $data['title'],
            $data['artist_name'],
            $data['cover_image_url'],
            $data['description'],
            $data['release_date'],
            $data['start_date'],
            json_encode($data['track_urls']),
            $data['status'],
            $data['updated_at'],
            $campaign->id,
        ]);

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

        return $campaign;
    }
}
