<?php
declare(strict_types=1);

namespace App\Users;

use App\Support\TranslatableException;
use InvalidArgumentException;
use PDO;
use RuntimeException;

final class ProfileService
{
    public function __construct(private readonly PDO $pdo) {}

    public function findPublicByUsername(string $username): ?array
    {
        $username = trim(rawurldecode($username));
        if ($username === '') return null;
        $statement = $this->pdo->prepare("SELECT id,name,username,bio,avatar_url,points,preferred_locale,status,account_type,trust_level,created_at,updated_at FROM users WHERE username=:username AND status='active' LIMIT 1");
        $statement->execute(['username' => $username]);
        $row = $statement->fetch(PDO::FETCH_ASSOC);
        return is_array($row) ? $row : null;
    }

    public function findForSettings(int $userId): ?array
    {
        $statement = $this->pdo->prepare('SELECT id,name,username,bio,avatar_url,email,role,status,account_type,trust_level,login_disabled,created_by_admin_id,points,preferred_locale,created_at FROM users WHERE id=:id LIMIT 1');
        $statement->execute(['id' => $userId]);
        $row = $statement->fetch(PDO::FETCH_ASSOC);
        return is_array($row) ? $row : null;
    }

    public function updateProfile(int $userId, string $name, string $username, string $bio, string $avatarUrl = ''): array
    {
        $name = trim($name);$username = trim($username);$bio = trim($bio);$avatarUrl = trim($avatarUrl);
        if ($name === '' || $this->length($name) > 100) throw new TranslatableException('profile.validation_name');
        if ($username !== '' && preg_match('/^[\p{L}\p{N}][\p{L}\p{N}._-]{2,39}$/u', $username) !== 1) throw new TranslatableException('profile.validation_username');
        if ($this->length($bio) > 500) throw new TranslatableException('profile.validation_bio_too_long');
        if ($avatarUrl !== '' && (filter_var($avatarUrl, FILTER_VALIDATE_URL) === false || !preg_match('~^https?://~i', $avatarUrl))) throw new TranslatableException('profile.validation_avatar_url');
        $this->requireActiveUser($userId);
        $current = $this->findForSettings($userId);
        if (!is_array($current)) throw new TranslatableException('profile.load_failed');
        if ($username === '') {
            $username = trim((string) ($current['username'] ?? ''));
            if ($username === '') {
                $username = $this->fallbackUsername($userId, (string) ($current['email'] ?? ''));
            }
        }
        $duplicate=$this->pdo->prepare('SELECT 1 FROM users WHERE username=:username AND id<>:id LIMIT 1');
        $duplicate->execute(['username'=>$username,'id'=>$userId]);
        if($duplicate->fetchColumn()!==false)throw new TranslatableException('profile.username_in_use');
        $statement=$this->pdo->prepare('UPDATE users SET name=:name,username=:username,bio=:bio,avatar_url=:avatar_url,updated_at=NOW() WHERE id=:id');
        $statement->execute(['name'=>$name,'username'=>$username,'bio'=>$bio===''?null:$bio,'avatar_url'=>$avatarUrl===''?null:$avatarUrl,'id'=>$userId]);
        $user=$this->findForSettings($userId);if(!is_array($user))throw new TranslatableException('profile.reload_failed');return $user;
    }

    /** @return array{questions:int,answers:int,topics:int,accepted_answers:int,received_votes:int} */
    public function summary(int $userId): array
    {
        $statement=$this->pdo->prepare("SELECT
            (SELECT COUNT(*) FROM questions q WHERE q.user_id=:uid1 AND q.status='published' AND q.is_anonymous=0) AS questions_count,
            (SELECT COUNT(*) FROM answers a INNER JOIN questions q ON q.id=a.question_id WHERE a.user_id=:uid2 AND a.status='published' AND q.status='published') AS answers_count,
            (SELECT COUNT(*) FROM topics t WHERE t.user_id=:uid6 AND t.status='published') AS topics_count,
            (SELECT COUNT(*) FROM answers a INNER JOIN questions q ON q.id=a.question_id WHERE a.user_id=:uid3 AND a.status='published' AND a.is_best=1 AND q.status='published') AS accepted_count,
            COALESCE((SELECT SUM(a.votes_count) FROM answers a INNER JOIN questions q ON q.id=a.question_id WHERE a.user_id=:uid4 AND a.status='published' AND q.status='published'),0) +
            COALESCE((SELECT SUM(q.votes_count) FROM questions q WHERE q.user_id=:uid5 AND q.status='published' AND q.is_anonymous=0),0) AS received_votes");
        $statement->execute(['uid1'=>$userId,'uid2'=>$userId,'uid3'=>$userId,'uid4'=>$userId,'uid5'=>$userId,'uid6'=>$userId]);
        $row=$statement->fetch(PDO::FETCH_ASSOC)?:[];
        return ['questions'=>(int)($row['questions_count']??0),'answers'=>(int)($row['answers_count']??0),'topics'=>(int)($row['topics_count']??0),'accepted_answers'=>(int)($row['accepted_count']??0),'received_votes'=>(int)($row['received_votes']??0)];
    }

    /** @return array{questions:list<array<string,mixed>>,answers:list<array<string,mixed>>,best_answers:list<array<string,mixed>>} */
    public function activity(int $userId, int $limit = 20): array
    {
        $limit=max(1,min(50,$limit));
        $questions=$this->pdo->prepare("SELECT id,slug,title,answers_count,votes_count,created_at FROM questions WHERE user_id=:user_id AND status='published' AND is_anonymous=0 ORDER BY created_at DESC,id DESC LIMIT {$limit}");
        $questions->execute(['user_id'=>$userId]);
        $answers=$this->pdo->prepare("SELECT a.id,a.question_id,a.body,a.votes_count,a.is_best,a.created_at,q.slug AS question_slug,q.title AS question_title,
            1+FLOOR((SELECT COUNT(*) FROM answers prior WHERE prior.question_id=a.question_id AND prior.status='published' AND (prior.is_best>a.is_best OR (prior.is_best=a.is_best AND (prior.created_at<a.created_at OR (prior.created_at=a.created_at AND prior.id<a.id)))))/50) AS answer_page
            FROM answers a INNER JOIN questions q ON q.id=a.question_id WHERE a.user_id=:user_id AND a.status='published' AND q.status='published' ORDER BY a.created_at DESC,a.id DESC LIMIT {$limit}");
        $answers->execute(['user_id'=>$userId]);
        return ['questions'=>$questions->fetchAll(PDO::FETCH_ASSOC),'answers'=>$answers->fetchAll(PDO::FETCH_ASSOC),'best_answers'=>$this->bestAnswers($userId,min(10,$limit))];
    }

    /** @return list<array<string,mixed>> */
    public function bestAnswers(int $userId, int $limit = 10): array
    {
        $limit=max(1,min(20,$limit));
        $statement=$this->pdo->prepare("SELECT a.id,a.question_id,a.body,a.votes_count,a.is_best,a.created_at,q.slug AS question_slug,q.title AS question_title,
            1+FLOOR((SELECT COUNT(*) FROM answers prior WHERE prior.question_id=a.question_id AND prior.status='published' AND (prior.is_best>a.is_best OR (prior.is_best=a.is_best AND (prior.created_at<a.created_at OR (prior.created_at=a.created_at AND prior.id<a.id)))))/50) AS answer_page
            FROM answers a INNER JOIN questions q ON q.id=a.question_id WHERE a.user_id=:user_id AND a.status='published' AND q.status='published' ORDER BY a.is_best DESC,a.votes_count DESC,a.created_at DESC,a.id DESC LIMIT {$limit}");
        $statement->execute(['user_id'=>$userId]);
        return $statement->fetchAll(PDO::FETCH_ASSOC);
    }

    private function fallbackUsername(int $userId, string $email): string
    {
        $preferred = 'member-' . $userId;
        $statement = $this->pdo->prepare('SELECT 1 FROM users WHERE username=:username AND id<>:id LIMIT 1');
        $statement->execute(['username'=>$preferred,'id'=>$userId]);
        if ($statement->fetchColumn() === false) return $preferred;
        return 'u-' . $userId . '-' . substr(hash('sha256', $userId . ':' . $email), 0, 8);
    }

    private function requireActiveUser(int $userId): void {$statement=$this->pdo->prepare("SELECT 1 FROM users WHERE id=:id AND status='active' LIMIT 1");$statement->execute(['id'=>$userId]);if($statement->fetchColumn()===false)throw new TranslatableException('auth.active_user_required');}
    private function length(string $value): int {if(function_exists('mb_strlen'))return mb_strlen($value,'UTF-8');$n=preg_match_all('/./us',$value,$m);return $n===false?strlen($value):$n;}
}
