<?php
declare(strict_types=1);

namespace App\Models;

use PDO;

class Comment extends BaseModel
{
    protected string $table = 'comments';

    public function listForContent(string $contentType, int $contentId, int $limit = 40, bool $includeModerationQueue = false, ?int $viewerId = null): array
    {
        $limit = min(max($limit, 1), 100);
        $statuses = $includeModerationQueue
            ? ['published', 'visible', 'pending_review']
            : ['published', 'visible'];

        $statusPlaceholders = [];
        $params = [
            'content_type' => $contentType,
            'content_id' => $contentId,
        ];

        foreach ($statuses as $index => $status) {
            $key = 'status_' . $index;
            $statusPlaceholders[] = ':' . $key;
            $params[$key] = $status;
        }

        $statement = $this->db()->prepare(
            'SELECT cm.id,
                    COALESCE(cm.content_type, cm.commentable_type) AS content_type,
                    COALESCE(cm.content_id, cm.commentable_id) AS content_id,
                    cm.user_id,
                    cm.parent_id,
                    COALESCE(cm.content, cm.body) AS content,
                    cm.status,
                    cm.risk_score,
                    cm.created_at,
                    cm.updated_at,
                    u.username,
                    COALESCE(up.display_name, u.nickname, u.username, "User") AS display_name,
                    up.avatar AS avatar,
                    (SELECT COUNT(*) FROM comment_likes cl WHERE cl.comment_id = cm.id) AS like_count,
                    (SELECT COUNT(*) FROM comment_likes clv WHERE clv.comment_id = cm.id AND clv.user_id = :viewer_id) AS viewer_liked
             FROM comments cm
             LEFT JOIN users u ON u.id = cm.user_id
             LEFT JOIN user_profiles up ON up.user_id = u.id
             WHERE COALESCE(cm.content_type, cm.commentable_type) = :content_type
               AND COALESCE(cm.content_id, cm.commentable_id) = :content_id
               AND cm.status IN (' . implode(',', $statusPlaceholders) . ')
             ORDER BY cm.parent_id IS NOT NULL ASC, cm.created_at DESC
             LIMIT :limit'
        );

        foreach ($params as $key => $value) {
            $statement->bindValue($key, $value, is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR);
        }
        $statement->bindValue('viewer_id', $viewerId ?? 0, PDO::PARAM_INT);
        $statement->bindValue('limit', $limit, PDO::PARAM_INT);
        $statement->execute();

        return array_map(fn (array $row): array => $this->normalize($row), $statement->fetchAll());
    }

    public function createForContent(string $contentType, int $contentId, int $userId, string $content, string $status, int $riskScore = 0, ?int $parentId = null): array
    {
        $status = $this->normalizeStatus($status);
        $riskScore = min(100, max(0, $riskScore));

        $statement = $this->db()->prepare(
            'INSERT INTO comments
                (content_type, content_id, commentable_type, commentable_id, user_id, parent_id, content, body, status, risk_score, created_at, updated_at)
             VALUES
                (:content_type, :content_id, :commentable_type, :commentable_id, :user_id, :parent_id, :content, :body, :status, :risk_score, NOW(), NOW())'
        );
        $statement->execute([
            'content_type' => $contentType,
            'content_id' => $contentId,
            'commentable_type' => $contentType,
            'commentable_id' => $contentId,
            'user_id' => $userId,
            'parent_id' => $parentId,
            'content' => $content,
            'body' => $content,
            'status' => $status,
            'risk_score' => $riskScore,
        ]);

        $commentId = (int) $this->db()->lastInsertId();
        $statement = $this->db()->prepare(
            'SELECT cm.id,
                    COALESCE(cm.content_type, cm.commentable_type) AS content_type,
                    COALESCE(cm.content_id, cm.commentable_id) AS content_id,
                    cm.user_id,
                    cm.parent_id,
                    COALESCE(cm.content, cm.body) AS content,
                    cm.status,
                    cm.risk_score,
                    cm.created_at,
                    cm.updated_at,
                    u.username,
                    COALESCE(up.display_name, u.nickname, u.username, "User") AS display_name,
                    up.avatar AS avatar,
                    (SELECT COUNT(*) FROM comment_likes cl WHERE cl.comment_id = cm.id) AS like_count,
                    0 AS viewer_liked
             FROM comments cm
             LEFT JOIN users u ON u.id = cm.user_id
             LEFT JOIN user_profiles up ON up.user_id = u.id
             WHERE cm.id = :id
             LIMIT 1'
        );
        $statement->execute(['id' => $commentId]);

        return $this->normalize($statement->fetch() ?: []);
    }

    public function tooManyRecent(int $userId, int $seconds = 60, int $max = 5): bool
    {
        $statement = $this->db()->prepare(
            'SELECT COUNT(*) FROM comments WHERE user_id = :user_id AND created_at >= :since'
        );
        $statement->bindValue('user_id', $userId, PDO::PARAM_INT);
        $statement->bindValue('since', date('Y-m-d H:i:s', time() - max(1, $seconds)), PDO::PARAM_STR);
        $statement->execute();

        return (int) $statement->fetchColumn() >= $max;
    }

    public function findDetailed(int $commentId): ?array
    {
        $statement = $this->db()->prepare(
            'SELECT cm.id,
                    COALESCE(cm.content_type, cm.commentable_type) AS content_type,
                    COALESCE(cm.content_id, cm.commentable_id) AS content_id,
                    cm.user_id,
                    cm.parent_id,
                    COALESCE(cm.content, cm.body) AS content,
                    cm.status,
                    cm.risk_score,
                    cm.created_at,
                    cm.updated_at,
                    u.username,
                    COALESCE(up.display_name, u.nickname, u.username, "User") AS display_name,
                    up.avatar AS avatar,
                    (SELECT COUNT(*) FROM comment_likes cl WHERE cl.comment_id = cm.id) AS like_count,
                    0 AS viewer_liked
             FROM comments cm
             LEFT JOIN users u ON u.id = cm.user_id
             LEFT JOIN user_profiles up ON up.user_id = u.id
             WHERE cm.id = :id
             LIMIT 1'
        );
        $statement->execute(['id' => $commentId]);
        $row = $statement->fetch();

        return $row ? $this->normalize($row) : null;
    }

    public function updateComment(int $commentId, int $userId, string $content, string $status, int $riskScore): ?array
    {
        $statement = $this->db()->prepare(
            'UPDATE comments
             SET content = :content,
                 body = :body,
                 status = :status,
                 risk_score = :risk_score,
                 updated_at = NOW()
             WHERE id = :id AND user_id = :user_id AND status <> "deleted"'
        );
        $statement->execute([
            'id' => $commentId,
            'user_id' => $userId,
            'content' => $content,
            'body' => $content,
            'status' => $this->normalizeStatus($status),
            'risk_score' => min(100, max(0, $riskScore)),
        ]);

        return $statement->rowCount() > 0 ? $this->findDetailed($commentId) : null;
    }

    public function softDelete(int $commentId): bool
    {
        $statement = $this->db()->prepare(
            'UPDATE comments
             SET status = "deleted",
                 content = "[silindi]",
                 body = "[silindi]",
                 updated_at = NOW()
             WHERE id = :id'
        );
        $statement->execute(['id' => $commentId]);

        return $statement->rowCount() > 0;
    }

    public function toggleLike(int $commentId, int $userId): array
    {
        $existing = $this->db()->prepare('SELECT id FROM comment_likes WHERE comment_id = :comment_id AND user_id = :user_id LIMIT 1');
        $existing->execute(['comment_id' => $commentId, 'user_id' => $userId]);
        $likeId = $existing->fetchColumn();

        if ($likeId) {
            $delete = $this->db()->prepare('DELETE FROM comment_likes WHERE id = :id');
            $delete->execute(['id' => (int) $likeId]);
            $liked = false;
        } else {
            $insert = $this->db()->prepare(
                'INSERT INTO comment_likes (comment_id, user_id, created_at)
                 VALUES (:comment_id, :user_id, NOW())'
            );
            $insert->execute(['comment_id' => $commentId, 'user_id' => $userId]);
            $liked = true;
        }

        $count = $this->db()->prepare('SELECT COUNT(*) FROM comment_likes WHERE comment_id = :comment_id');
        $count->execute(['comment_id' => $commentId]);

        return ['liked' => $liked, 'like_count' => (int) $count->fetchColumn()];
    }

    private function normalize(array $row): array
    {
        $content = (string) ($row['content'] ?? '');

        return [
            'id' => (int) ($row['id'] ?? 0),
            'content_type' => (string) ($row['content_type'] ?? ''),
            'content_id' => (int) ($row['content_id'] ?? 0),
            'user_id' => (int) ($row['user_id'] ?? 0),
            'parent_id' => isset($row['parent_id']) && $row['parent_id'] !== null ? (int) $row['parent_id'] : null,
            'content' => $content,
            'status' => (string) ($row['status'] ?? 'published'),
            'risk_score' => (int) ($row['risk_score'] ?? 0),
            'created_at' => (string) ($row['created_at'] ?? ''),
            'updated_at' => (string) ($row['updated_at'] ?? ''),
            'username' => (string) ($row['username'] ?? 'user'),
            'display_name' => (string) ($row['display_name'] ?? 'User'),
            'avatar' => $row['avatar'] ?? null,
            'like_count' => (int) ($row['like_count'] ?? 0),
            'viewer_liked' => (int) ($row['viewer_liked'] ?? 0) > 0,
        ];
    }

    private function normalizeStatus(string $status): string
    {
        return in_array($status, ['published', 'pending_review', 'blocked', 'visible', 'hidden', 'pending', 'deleted'], true)
            ? $status
            : 'published';
    }
}
