<?php
declare(strict_types=1);

namespace App\Models;

use PDO;

class Story extends BaseModel
{
    protected string $table = 'stories';

    public function createStory(int $userId, string $type, string $content, ?string $mediaPath, ?string $linkUrl): array
    {
        $type = $type === 'image' ? 'image' : 'text';
        $statement = $this->db()->prepare(
            'INSERT INTO stories
                (user_id, story_type, content, media_path, link_url, status, expires_at, created_at)
             VALUES
                (:user_id, :story_type, :content, :media_path, :link_url, "active", DATE_ADD(NOW(), INTERVAL 24 HOUR), NOW())'
        );
        $statement->execute([
            'user_id' => $userId,
            'story_type' => $type,
            'content' => trim($content) !== '' ? trim($content) : null,
            'media_path' => $mediaPath,
            'link_url' => $linkUrl,
        ]);

        return $this->findActive((int) $this->db()->lastInsertId(), $userId) ?? [];
    }

    public function listActiveFeed(?int $viewerId, int $limit = 18): array
    {
        $limit = min(max($limit, 1), 60);
        $viewerId = (int) ($viewerId ?? 0);
        $statement = $this->db()->prepare(
            $this->baseSelect() .
            ' WHERE s.status = "active" AND s.deleted_at IS NULL AND s.expires_at > NOW()
              ORDER BY
                CASE WHEN :viewer_order > 0 AND (
                    s.user_id = :viewer_self
                    OR EXISTS (SELECT 1 FROM user_follows uf WHERE uf.follower_id = :viewer_follow AND uf.followed_id = s.user_id)
                    OR EXISTS (
                        SELECT 1 FROM friendships f
                        WHERE f.status = "accepted"
                          AND (
                            (f.requester_id = :viewer_friend_a AND f.receiver_id = s.user_id)
                            OR (f.receiver_id = :viewer_friend_b AND f.requester_id = s.user_id)
                          )
                    )
                ) THEN 0 ELSE 1 END,
                s.created_at DESC
              LIMIT :limit'
        );
        foreach (['viewer_order', 'viewer_self', 'viewer_follow', 'viewer_friend_a', 'viewer_friend_b', 'viewer_seen'] as $key) {
            $statement->bindValue($key, $viewerId, 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 activeForUser(int $userId, ?int $viewerId = null): ?array
    {
        $statement = $this->db()->prepare(
            $this->baseSelect() .
            ' WHERE s.user_id = :user_id
                AND s.status = "active"
                AND s.deleted_at IS NULL
                AND s.expires_at > NOW()
              ORDER BY s.created_at DESC
              LIMIT 1'
        );
        $statement->bindValue('user_id', $userId, PDO::PARAM_INT);
        $statement->bindValue('viewer_seen', (int) ($viewerId ?? 0), PDO::PARAM_INT);
        $statement->execute();
        $row = $statement->fetch();

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

    public function listActiveForUser(int $userId, ?int $viewerId = null): array
    {
        $statement = $this->db()->prepare(
            $this->baseSelect() .
            ' WHERE s.user_id = :user_id
                AND s.status = "active"
                AND s.deleted_at IS NULL
                AND s.expires_at > NOW()
              ORDER BY s.created_at ASC
              LIMIT 20'
        );
        $statement->bindValue('user_id', $userId, PDO::PARAM_INT);
        $statement->bindValue('viewer_seen', (int) ($viewerId ?? 0), PDO::PARAM_INT);
        $statement->execute();

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

    public function findActive(int $storyId, ?int $viewerId = null): ?array
    {
        $statement = $this->db()->prepare(
            $this->baseSelect() .
            ' WHERE s.id = :id
                AND s.status = "active"
                AND s.deleted_at IS NULL
                AND s.expires_at > NOW()
              LIMIT 1'
        );
        $statement->bindValue('id', $storyId, PDO::PARAM_INT);
        $statement->bindValue('viewer_seen', (int) ($viewerId ?? 0), PDO::PARAM_INT);
        $statement->execute();
        $row = $statement->fetch();

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

    public function markViewed(int $storyId, int $userId): void
    {
        $this->db()->prepare(
            'INSERT INTO story_views (story_id, user_id, viewed_at)
             VALUES (:story_id, :user_id, NOW())
             ON DUPLICATE KEY UPDATE viewed_at = NOW()'
        )->execute(['story_id' => $storyId, 'user_id' => $userId]);
    }

    public function softDelete(int $storyId, int $userId, bool $isAdmin = false): bool
    {
        $sql = 'UPDATE stories SET status = "deleted", deleted_at = NOW() WHERE id = :id';
        $params = ['id' => $storyId];
        if (!$isAdmin) {
            $sql .= ' AND user_id = :user_id';
            $params['user_id'] = $userId;
        }

        $statement = $this->db()->prepare($sql);
        $statement->execute($params);

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

    private function baseSelect(): string
    {
        return 'SELECT s.*,
                       u.username,
                       COALESCE(NULLIF(up.display_name, ""), NULLIF(u.nickname, ""), u.username, "User") AS display_name,
                       COALESCE(NULLIF(up.nickname, ""), u.username) AS nickname,
                       COALESCE(NULLIF(up.avatar, ""), "") AS avatar,
                       (SELECT COUNT(*) FROM story_views sv WHERE sv.story_id = s.id) AS view_count,
                       (SELECT COUNT(*) FROM story_views sv WHERE sv.story_id = s.id AND sv.user_id = :viewer_seen) AS viewer_seen
                FROM stories s
                LEFT JOIN users u ON u.id = s.user_id
                LEFT JOIN user_profiles up ON up.user_id = u.id';
    }

    private function normalize(array $row): array
    {
        return [
            'id' => (int) ($row['id'] ?? 0),
            'user_id' => (int) ($row['user_id'] ?? 0),
            'story_type' => (string) ($row['story_type'] ?? 'text'),
            'content' => (string) ($row['content'] ?? ''),
            'media_path' => (string) ($row['media_path'] ?? ''),
            'link_url' => (string) ($row['link_url'] ?? ''),
            'status' => (string) ($row['status'] ?? 'active'),
            'expires_at' => (string) ($row['expires_at'] ?? ''),
            'created_at' => (string) ($row['created_at'] ?? ''),
            'deleted_at' => $row['deleted_at'] ?? null,
            'username' => (string) ($row['username'] ?? 'user'),
            'display_name' => (string) ($row['display_name'] ?? 'User'),
            'nickname' => (string) ($row['nickname'] ?? $row['username'] ?? 'user'),
            'avatar' => (string) ($row['avatar'] ?? ''),
            'view_count' => (int) ($row['view_count'] ?? 0),
            'viewer_seen' => (int) ($row['viewer_seen'] ?? 0) > 0,
        ];
    }
}
