<?php

class UserRating
{
    private PDO $db;

    public function __construct()
    {
        $this->db = Database::getInstance();
    }

    public function getAverageForUser(int $userId): ?array
    {
        $stmt = $this->db->prepare(
            'SELECT AVG(rating) as average, COUNT(*) as count FROM user_ratings WHERE rated_user_id = :user_id'
        );
        $stmt->execute(['user_id' => $userId]);
        $row = $stmt->fetch();
        if (!$row || (int)$row['count'] === 0) return null;
        return [
            'average' => round((float)$row['average'], 1),
            'count' => (int)$row['count'],
        ];
    }

    public function getByUser(int $userId, int $limit = 20): array
    {
        $stmt = $this->db->prepare(
            'SELECT ur.*, u.username, u.display_name, u.avatar
             FROM user_ratings ur
             JOIN users u ON ur.rater_id = u.id
             WHERE ur.rated_user_id = :user_id
             ORDER BY ur.created_at DESC LIMIT ' . $limit
        );
        $stmt->execute(['user_id' => $userId]);
        return $stmt->fetchAll();
    }

    public function create(array $data): int
    {
        $stmt = $this->db->prepare(
            'INSERT INTO user_ratings (rater_id, rated_user_id, listing_id, rating, comment)
             VALUES (:rater_id, :rated_user_id, :listing_id, :rating, :comment)
             ON DUPLICATE KEY UPDATE rating = :rating2, comment = :comment2'
        );
        $stmt->execute([
            'rater_id'      => $data['rater_id'],
            'rated_user_id' => $data['rated_user_id'],
            'listing_id'    => $data['listing_id'] ?? null,
            'rating'        => $data['rating'],
            'comment'       => $data['comment'] ?? null,
            'rating2'       => $data['rating'],
            'comment2'      => $data['comment'] ?? null,
        ]);
        return (int)$this->db->lastInsertId();
    }

    public function hasRated(int $raterId, int $listingId): bool
    {
        $stmt = $this->db->prepare('SELECT COUNT(*) FROM user_ratings WHERE rater_id = :rater_id AND listing_id = :listing_id');
        $stmt->execute(['rater_id' => $raterId, 'listing_id' => $listingId]);
        return (int)$stmt->fetchColumn() > 0;
    }

    /**
     * Fetch a specific rating row submitted by $raterId for $listingId, if any.
     */
    public function getRatingByRaterForListing(int $raterId, int $listingId): ?array
    {
        $stmt = $this->db->prepare(
            'SELECT * FROM user_ratings WHERE rater_id = :rater_id AND listing_id = :listing_id LIMIT 1'
        );
        $stmt->execute(['rater_id' => $raterId, 'listing_id' => $listingId]);
        $row = $stmt->fetch();
        return $row ?: null;
    }

    /**
     * Number of days a rating remains open after a listing is marked sold.
     */
    public const RATING_WINDOW_DAYS = 30;

    /**
     * Determine whether $raterId may rate $rateeId for the given $listing array.
     * Returns ['allowed' => bool, 'reason' => string|null].
     *
     * The $listing array must contain: id, user_id (seller), buyer_id, status, sold_at.
     */
    public function canRate(int $raterId, int $rateeId, array $listing): array
    {
        if (($listing['status'] ?? '') !== 'sold') {
            return ['allowed' => false, 'reason' => 'not_sold'];
        }
        $sellerId = (int)($listing['user_id'] ?? 0);
        $buyerId  = isset($listing['buyer_id']) && $listing['buyer_id'] !== null ? (int)$listing['buyer_id'] : 0;
        if ($buyerId === 0) {
            return ['allowed' => false, 'reason' => 'no_buyer_recorded'];
        }
        $validPair = ($raterId === $sellerId && $rateeId === $buyerId)
                  || ($raterId === $buyerId && $rateeId === $sellerId);
        if (!$validPair) {
            return ['allowed' => false, 'reason' => 'not_party'];
        }
        $soldAt = $listing['sold_at'] ?? null;
        if (!$soldAt) {
            return ['allowed' => false, 'reason' => 'no_sold_at'];
        }
        $deadline = strtotime($soldAt) + (self::RATING_WINDOW_DAYS * 86400);
        if (time() > $deadline) {
            return ['allowed' => false, 'reason' => 'window_expired'];
        }
        if ($this->hasRated($raterId, (int)$listing['id'])) {
            return ['allowed' => false, 'reason' => 'already_rated'];
        }
        return ['allowed' => true, 'reason' => null];
    }
}
