<?php
declare(strict_types=1);

namespace App\Core;

use App\Models\Subscription;

class Auth
{
    private const SESSION_KEY = 'auth_user';

    public static function user(): ?array
    {
        return Session::get(self::SESSION_KEY);
    }

    public static function id(): ?int
    {
        $user = self::user();

        return isset($user['id']) ? (int) $user['id'] : null;
    }

    public static function check(): bool
    {
        return self::user() !== null;
    }

    public static function role(string $role): bool
    {
        return (self::user()['role'] ?? 'guest') === $role;
    }

    public static function login(array $user): void
    {
        Session::regenerate();
        unset($user['password_hash']);
        $user = self::withProfile($user);
        Session::put(self::SESSION_KEY, $user);

        if (!empty($user['language'])) {
            Session::put('lang', $user['language']);
            $secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
            setcookie('lang', (string) $user['language'], [
                'expires' => time() + 31536000,
                'path' => '/',
                'secure' => $secure,
                'httponly' => false,
                'samesite' => 'Lax',
            ]);
        }
    }

    public static function attempt(string $identifier, string $password, array $config = []): bool
    {
        $pdo = Database::connection($config ?: null);
        $identifier = strtolower(trim($identifier));
        $statement = $pdo->prepare('SELECT * FROM users WHERE (LOWER(email) = :email_identifier OR LOWER(username) = :username_identifier) AND status = "active" LIMIT 1');
        $statement->execute([
            'email_identifier' => $identifier,
            'username_identifier' => $identifier,
        ]);
        $user = $statement->fetch();

        if (!$user || !password_verify($password, (string) $user['password_hash'])) {
            return false;
        }

        self::login($user);
        $update = $pdo->prepare('UPDATE users SET last_login_at = NOW(), updated_at = NOW() WHERE id = :id');
        $update->execute(['id' => (int) $user['id']]);

        return true;
    }

    public static function logout(): void
    {
        Session::destroy();
    }

    public static function setSessionLanguage(string $lang): void
    {
        $user = self::user();
        if (!$user) {
            return;
        }

        $user['language'] = $lang;
        Session::put(self::SESSION_KEY, $user);
    }

    public static function refreshSessionProfile(array $profile): void
    {
        $user = self::user();
        if (!$user) {
            return;
        }

        $user['display_name'] = $profile['display_name'] ?? ($user['display_name'] ?? null);
        $user['nickname'] = $profile['display_name'] ?? ($user['nickname'] ?? null);
        $user['profile_nickname'] = $profile['nickname'] ?? ($user['profile_nickname'] ?? null);
        $user['avatar'] = $profile['avatar'] ?? ($user['avatar'] ?? null);
        Session::put(self::SESSION_KEY, $user);
    }

    private static function withProfile(array $user): array
    {
        if (empty($user['id'])) {
            return $user;
        }

        try {
            $pdo = Database::connection();
            $statement = $pdo->prepare('SELECT display_name, avatar FROM user_profiles WHERE user_id = :user_id LIMIT 1');
            $statement->execute(['user_id' => (int) $user['id']]);
            $profile = $statement->fetch();

            if ($profile) {
                $user['display_name'] = $profile['display_name'] ?: ($user['nickname'] ?? $user['username'] ?? null);
                $user['avatar'] = $profile['avatar'] ?? null;
            }
        } catch (\Throwable) {
            $user['display_name'] = $user['nickname'] ?? $user['username'] ?? null;
        }

        return $user;
    }

    public static function requireUser(): void
    {
        if (!self::check()) {
            header('Location: ' . url('/giris-yap'));
            exit;
        }
    }

    public static function requireRole(string $role): void
    {
        if (!self::role($role)) {
            http_response_code(403);
            echo View::page('main', 'pages/show', [
                'title' => '403',
                'page' => [
                    'title' => 'Yetkisiz erisim',
                    'content' => 'Bu alani goruntulemek icin gerekli yetkiye sahip degilsiniz.',
                ],
            ]);
            exit;
        }
    }

    public static function requireAdmin(): void
    {
        self::requireRole('admin');
    }

    public static function hasActiveSubscription(int $subscriberId, int $creatorId, ?int $packageId = null): bool
    {
        if ($subscriberId <= 0 || $creatorId <= 0) {
            return false;
        }

        try {
            return (new Subscription())->hasActive($subscriberId, $creatorId, $packageId);
        } catch (\Throwable) {
            return false;
        }
    }

    public static function hasActiveSiteSubscription(int $userId): bool
    {
        if ($userId <= 0) {
            return false;
        }

        try {
            $statement = Database::connection()->prepare(
                'SELECT 1
                 FROM site_subscriptions ss
                 INNER JOIN site_plans sp ON sp.id = ss.site_plan_id
                 WHERE ss.user_id = :user_id
                   AND ss.status = "active"
                   AND sp.status = "active"
                   AND (ss.starts_at IS NULL OR ss.starts_at <= NOW())
                   AND (ss.ends_at IS NULL OR ss.ends_at > NOW())
                 LIMIT 1'
            );
            $statement->execute(['user_id' => $userId]);

            return (bool) $statement->fetchColumn();
        } catch (\Throwable) {
            return false;
        }
    }

    public static function userCanAccessMod(mixed $userIdOrMod, ?array $mod = null): bool
    {
        if (is_array($userIdOrMod) && $mod === null) {
            $mod = $userIdOrMod;
            $userId = self::id() ?? 0;
        } else {
            $userId = (int) $userIdOrMod;
            $mod ??= [];
        }

        $privacy = (string) ($mod['privacy'] ?? 'public');
        $accessType = (string) ($mod['access_type'] ?? 'free');

        if (in_array($privacy, ['public', 'free'], true) && $accessType === 'free' && empty($mod['package_id'])) {
            return true;
        }

        if ($userId <= 0) {
            return false;
        }

        if (self::roleForUser($userId) === 'admin') {
            return true;
        }

        $creatorId = (int) (($mod['creator_id'] ?? null) ?: ($mod['user_id'] ?? 0));
        if ($userId > 0 && in_array($userId, [(int) ($mod['user_id'] ?? 0), $creatorId], true)) {
            return true;
        }

        return self::hasActiveSubscription($userId, $creatorId, self::packageId($mod));
    }

    public static function userCanAccessTrack(mixed $userIdOrTrack, ?array $track = null): bool
    {
        if (is_array($userIdOrTrack) && $track === null) {
            $track = $userIdOrTrack;
            $userId = self::id() ?? 0;
        } else {
            $userId = (int) $userIdOrTrack;
            $track ??= [];
        }

        $privacy = (string) ($track['privacy'] ?? 'public');

        if (in_array($privacy, ['public', 'free'], true) && empty($track['package_id'])) {
            return true;
        }

        if ($userId <= 0) {
            return false;
        }

        if (self::roleForUser($userId) === 'admin') {
            return true;
        }

        $creatorId = (int) ($track['user_id'] ?? $track['creator_id'] ?? 0);
        if ($userId > 0 && $userId === $creatorId) {
            return true;
        }

        return self::hasActiveSiteSubscription($userId);
    }

    private static function roleForUser(int $userId): string
    {
        $current = self::user();
        if ($current && (int) ($current['id'] ?? 0) === $userId) {
            return (string) ($current['role'] ?? 'user');
        }

        try {
            $pdo = Database::connection();
            $statement = $pdo->prepare('SELECT role FROM users WHERE id = :id AND status = "active" LIMIT 1');
            $statement->execute(['id' => $userId]);

            return (string) ($statement->fetchColumn() ?: 'user');
        } catch (\Throwable) {
            return 'user';
        }
    }

    private static function packageId(array $content): ?int
    {
        $packageId = (int) ($content['package_id'] ?? 0);

        return $packageId > 0 ? $packageId : null;
    }
}
