<?php

declare(strict_types=1);

namespace Flowaxy\Infrastructure\Persistence\Repositories;

use Flowaxy\Domain\User\Entities\AdminUser;
use Flowaxy\Contracts\Domain\User\AdminUserRepositoryInterface;
use Flowaxy\Support\Helpers\DatabaseHelper;
use Flowaxy\Support\Facades\Log;
use PDO;
use Exception;
use Throwable;

final class AdminUserRepository implements AdminUserRepositoryInterface
{
    private ?PDO $connection = null;

    public function __construct()
    {
        try {
            // Используем DatabaseHelper для получения соединения
            $this->connection = DatabaseHelper::getConnection();
        } catch (Throwable $e) {
            // Логируем ошибку, если Log facade доступен
            try {
                Log::Error('Помилка підключення до бази даних в AdminUserRepository', [
                    'error' => $e->getMessage(),
                    'code' => $e->getCode(),
                    'exception' => $e
                ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
        }
    }

    // Найти пользователя по имени
    public function findByUsername(string $username): ?AdminUser
    {
        if ($this->connection === null) {
            try {
                Log::Debug('AdminUserRepository::findByUsername: Connection is null', ['username' => $username]);
            } catch (Throwable $e) {
                // Игнорируем ошибки логирования
            }
            return null;
        }

        try {
            try {
                Log::Debug('AdminUserRepository::findByUsername: Searching for user', ['username' => $username]);
            } catch (Throwable $e) {
                // Игнорируем ошибки логирования
            }

            $stmt = $this->connection->prepare('SELECT id, username, password, session_token, last_activity, is_active FROM users WHERE username = ? LIMIT 1');
            $stmt->execute([$username]);
            $row = $stmt->fetch(PDO::FETCH_ASSOC);

            if ($row) {
                try {
                    Log::Debug('AdminUserRepository::findByUsername: User found', [
                        'username' => $row['username'],
                        'user_id' => $row['id'],
                    ]);
                } catch (Throwable $e) {
                    // Игнорируем ошибки логирования
                }
                try {
                    Log::Info('AdminUserRepository::findByUsername: User retrieved successfully', ['user_id' => $row['id']]);
                } catch (Throwable $e) {
                    // Игнорируем ошибки логирования
                }
                return $this->mapRow($row);
            } else {
                try {
                    Log::Debug('AdminUserRepository::findByUsername: User not found', ['username' => $username]);
                } catch (Throwable $e) {
                    // Игнорируем ошибки логирования
                }
                return null;
            }
        } catch (Exception $e) {
            try {
                Log::Error('Помилка пошуку користувача за іменем', [
                    'error' => $e->getMessage(),
                    'username' => $username,
                    'exception' => $e
                ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
            return null;
        }
    }

    // Найти пользователя по ID
    public function findById(int $id): ?AdminUser
    {
        if ($this->connection === null) {
            Log::Debug('AdminUserRepository::findById: Connection is null', ['user_id' => $id]);
            return null;
        }

        try {
            Log::Debug('AdminUserRepository::findById: Searching for user', ['user_id' => $id]);

            $stmt = $this->connection->prepare('SELECT id, username, password, session_token, last_activity, is_active FROM users WHERE id = ? LIMIT 1');
            $stmt->execute([$id]);
            $row = $stmt->fetch(PDO::FETCH_ASSOC);

            if ($row) {
                Log::Info('AdminUserRepository::findById: User retrieved successfully', ['user_id' => $id]);
                return $this->mapRow($row);
            } else {
                Log::Debug('AdminUserRepository::findById: User not found', ['user_id' => $id]);
                return null;
            }
        } catch (Exception $e) {
            try {
                Log::Error('Помилка пошуку користувача за ID', [
                    'error' => $e->getMessage(),
                    'user_id' => $id,
                    'exception' => $e
                ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
            return null;
        }
    }

    public function updateSession(int $userId, string $token, string $lastActivity): bool
    {
        if ($this->connection === null) {
            if (function_exists('logWarning')) {
                // Connection is null - это не критическая ошибка, просто возвращаем false
            }
            return false;
        }

        try {
            if (function_exists('logDebug')) {
                Log::Debug('AdminUserRepository::updateSession: Updating session', ['user_id' => $userId]);
            }

            $stmt = $this->connection->prepare('UPDATE users SET session_token = ?, last_activity = ?, is_active = 1 WHERE id = ?');
            $result = $stmt->execute([$token, $lastActivity, $userId]);

            if ($result) {
                Log::Info('AdminUserRepository::updateSession: Session updated successfully', ['user_id' => $userId]);
            }

            return $result;
        } catch (Exception $e) {
            try {
                Log::Error('Помилка оновлення сесії користувача', [
                    'error' => $e->getMessage(),
                    'user_id' => $userId,
                    'exception' => $e
                ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
            return false;
        }
    }

    public function clearSession(int $userId): bool
    {
        if ($this->connection === null) {
            // Connection is null - это не критическая ошибка, просто возвращаем false
            return false;
        }

        try {
            Log::Debug('AdminUserRepository::clearSession: Clearing session', ['user_id' => $userId]);

            $stmt = $this->connection->prepare('UPDATE users SET session_token = NULL, last_activity = NULL, is_active = 0 WHERE id = ?');
            $result = $stmt->execute([$userId]);

            if ($result) {
                Log::Info('AdminUserRepository::clearSession: Session cleared successfully', ['user_id' => $userId]);
            }

            return $result;
        } catch (Exception $e) {
            try {
                Log::Error('Помилка очищення сесії користувача', [
                        'error' => $e->getMessage(),
                        'user_id' => $userId,
                        'exception' => $e
                    ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
            return false;
        }
    }

    public function markInactive(int $userId): bool
    {
        if ($this->connection === null) {
            // Connection is null - это не критическая ошибка, просто возвращаем false
            return false;
        }

        try {
            Log::Debug('AdminUserRepository::markInactive: Marking user as inactive', ['user_id' => $userId]);

            $stmt = $this->connection->prepare('UPDATE users SET is_active = 0 WHERE id = ?');
            $result = $stmt->execute([$userId]);

            if ($result) {
                Log::Info('AdminUserRepository::markInactive: User marked as inactive', ['user_id' => $userId]);
            }

            return $result;
        } catch (Exception $e) {
            try {
                Log::Error('Помилка деактивації користувача', [
                        'error' => $e->getMessage(),
                        'user_id' => $userId,
                        'exception' => $e
                    ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
            return false;
        }
    }

    public function updateLastActivity(int $userId, string $timestamp): bool
    {
        if ($this->connection === null) {
            // Connection is null - это не критическая ошибка, просто возвращаем false
            return false;
        }

        try {
            Log::Debug('AdminUserRepository::updateLastActivity: Updating last activity', [
                'user_id' => $userId,
                'timestamp' => $timestamp,
            ]);

            $stmt = $this->connection->prepare('UPDATE users SET last_activity = ?, is_active = 1 WHERE id = ?');
            $result = $stmt->execute([$timestamp, $userId]);

            if ($result) {
                Log::Info('AdminUserRepository::updateLastActivity: Last activity updated', ['user_id' => $userId]);
            }

            return $result;
        } catch (Exception $e) {
            try {
                Log::Error('Помилка оновлення останньої активності користувача', [
                        'error' => $e->getMessage(),
                        'user_id' => $userId,
                        'timestamp' => $timestamp,
                        'exception' => $e
                    ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
            return false;
        }
    }

    /**
     * Найти пользователя по email
     * Примечание: если в таблице нет поля email, возвращает null
     *
     * @param string $email
     * @return AdminUser|null
     */
    public function findByEmail(string $email): ?AdminUser
    {
        if ($this->connection === null) {
            try {
                Log::Debug('AdminUserRepository::findByEmail: Connection is null', ['email' => $email]);
            } catch (Throwable $e) {
                // Игнорируем ошибки логирования
            }
            return null;
        }

        try {
            // Пробуем найти по email, если поле существует
            // Если поля нет, запрос вернет пустой результат без ошибки
            $stmt = $this->connection->prepare('SELECT id, username, password, session_token, last_activity, is_active FROM users WHERE email = ? LIMIT 1');
            $stmt->execute([$email]);
            $row = $stmt->fetch(PDO::FETCH_ASSOC);

            if ($row) {
                return $this->mapRow($row);
            }

            // Если не найдено по email, возвращаем null
            // Это нормально, если в таблице нет поля email
            return null;
        } catch (Exception $e) {
            // Если ошибка связана с отсутствием поля email, просто возвращаем null
            // Иначе логируем ошибку
            $errorMessage = $e->getMessage();
            if (str_contains($errorMessage, "Unknown column 'email'") ||
                str_contains($errorMessage, "column 'email' does not exist")) {
                // Поле email отсутствует в таблице - это нормально для админки
                return null;
            }

            try {
                Log::Error('Помилка пошуку користувача за email', [
                    'error' => $errorMessage,
                    'email' => $email,
                    'exception' => $e
                ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
            return null;
        }
    }

    /**
     * Создать пользователя
     *
     * @param array<string, mixed> $data
     * @return AdminUser
     */
    public function create(array $data): AdminUser
    {
        if ($this->connection === null) {
            throw new Exception('Database connection is not available');
        }

        try {
            $username = $data['username'] ?? '';
            $password = $data['password'] ?? $data['password_hash'] ?? '';
            $isActive = isset($data['is_active']) ? (bool)$data['is_active'] : true;

            $stmt = $this->connection->prepare(
                'INSERT INTO users (username, password, is_active) VALUES (?, ?, ?)'
            );
            $stmt->execute([$username, $password, $isActive ? 1 : 0]);

            $userId = (int)$this->connection->lastInsertId();

            return new AdminUser(
                id: $userId,
                username: $username,
                passwordHash: $password,
                sessionToken: null,
                lastActivity: null,
                isActive: $isActive
            );
        } catch (Exception $e) {
            try {
                Log::Error('Помилка створення користувача', [
                    'error' => $e->getMessage(),
                    'data' => $data,
                    'exception' => $e
                ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
            throw new Exception('Failed to create user: ' . $e->getMessage(), 0, $e);
        }
    }

    /**
     * Обновить пользователя
     *
     * @param int $id
     * @param array<string, mixed> $data
     * @return bool
     */
    public function update(int $id, array $data): bool
    {
        if ($this->connection === null) {
            try {
                Log::Debug('AdminUserRepository::update: Connection is null', ['user_id' => $id]);
            } catch (Throwable $e) {
                // Игнорируем ошибки логирования
            }
            return false;
        }

        try {
            $updates = [];
            $params = [];

            if (isset($data['username'])) {
                $updates[] = 'username = ?';
                $params[] = $data['username'];
            }

            if (isset($data['password']) || isset($data['password_hash'])) {
                $updates[] = 'password = ?';
                $params[] = $data['password'] ?? $data['password_hash'];
            }

            if (isset($data['is_active'])) {
                $updates[] = 'is_active = ?';
                $params[] = (bool)$data['is_active'] ? 1 : 0;
            }

            if (isset($data['session_token'])) {
                $updates[] = 'session_token = ?';
                $params[] = $data['session_token'];
            }

            if (isset($data['last_activity'])) {
                $updates[] = 'last_activity = ?';
                $params[] = $data['last_activity'];
            }

            if (empty($updates)) {
                return false;
            }

            $params[] = $id;
            $sql = 'UPDATE users SET ' . implode(', ', $updates) . ' WHERE id = ?';
            $stmt = $this->connection->prepare($sql);
            return $stmt->execute($params);
        } catch (Exception $e) {
            try {
                Log::Error('Помилка оновлення користувача', [
                    'error' => $e->getMessage(),
                    'user_id' => $id,
                    'data' => $data,
                    'exception' => $e
                ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
            return false;
        }
    }

    /**
     * Удалить пользователя
     *
     * @param int $id
     * @return bool
     */
    public function delete(int $id): bool
    {
        if ($this->connection === null) {
            try {
                Log::Debug('AdminUserRepository::delete: Connection is null', ['user_id' => $id]);
            } catch (Throwable $e) {
                // Игнорируем ошибки логирования
            }
            return false;
        }

        try {
            $stmt = $this->connection->prepare('DELETE FROM users WHERE id = ?');
            return $stmt->execute([$id]);
        } catch (Exception $e) {
            try {
                Log::Error('Помилка видалення користувача', [
                    'error' => $e->getMessage(),
                    'user_id' => $id,
                    'exception' => $e
                ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
            return false;
        }
    }

    /**
     * Найти всех пользователей
     *
     * @param array<string, mixed> $criteria
     * @return array<int, AdminUser>
     */
    public function findAll(array $criteria = []): array
    {
        if ($this->connection === null) {
            try {
                Log::Debug('AdminUserRepository::findAll: Connection is null');
            } catch (Throwable $e) {
                // Игнорируем ошибки логирования
            }
            return [];
        }

        try {
            $sql = 'SELECT id, username, password, session_token, last_activity, is_active FROM users';
            $params = [];

            if (!empty($criteria)) {
                $conditions = [];
                foreach ($criteria as $key => $value) {
                    if ($key === 'username') {
                        $conditions[] = 'username = ?';
                        $params[] = $value;
                    } elseif ($key === 'is_active') {
                        $conditions[] = 'is_active = ?';
                        $params[] = (bool)$value ? 1 : 0;
                    }
                }

                if (!empty($conditions)) {
                    $sql .= ' WHERE ' . implode(' AND ', $conditions);
                }
            }

            $stmt = $this->connection->prepare($sql);
            $stmt->execute($params);
            $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

            $users = [];
            foreach ($rows as $row) {
                $users[] = $this->mapRow($row);
            }

            return $users;
        } catch (Exception $e) {
            try {
                Log::Error('Помилка пошуку користувачів', [
                    'error' => $e->getMessage(),
                    'criteria' => $criteria,
                    'exception' => $e
                ]);
            } catch (Throwable $logError) {
                // Игнорируем ошибки логирования
            }
            return [];
        }
    }

    /**
     * Маппінг рядка з БД до об'єкта AdminUser
     *
     * @param array<string, mixed> $row Рядок з БД
     * @return AdminUser
     */
    private function mapRow(array $row): AdminUser
    {
        return new AdminUser(
            id: (int)$row['id'],
            username: $row['username'],
            passwordHash: $row['password'],
            sessionToken: $row['session_token'] ?? null,
            lastActivity: $row['last_activity'] ?? null,
            isActive: (bool)$row['is_active']
        );
    }
}
