<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\FileStore;

class AuthService
{
    private FileStore $users;
    private ActivityLogService $logs;

    public function __construct()
    {
        $this->users = new FileStore(storage_path('data/users.json'));
        $this->logs = new ActivityLogService();
        $this->seedAdmin();
    }

    public function register(array $data): array
    {
        $errors = $this->validateRegistration($data);

        if ($errors) {
            return ['ok' => false, 'errors' => $errors];
        }

        $role = $data['role'];
        $status = in_array($role, ['instructor', 'institution'], true) ? 'pending' : 'active';

        $user = $this->users->insert([
            'name' => trim($data['name']),
            'email' => strtolower(trim($data['email'])),
            'phone' => trim($data['phone'] ?? ''),
            'password' => password_hash($data['password'], PASSWORD_DEFAULT),
            'role' => $role,
            'status' => $status,
            'profile' => [
                'province' => trim($data['province'] ?? ''),
                'city' => trim($data['city'] ?? ''),
                'bio' => '',
            ],
            'password_reset_token' => null,
            'created_at' => date('c'),
            'updated_at' => date('c'),
            'last_login_at' => null,
        ]);

        $this->logs->log('user_registered', "Conta criada com perfil {$role}.", (int) $user['id']);

        return ['ok' => true, 'user' => $this->withoutPassword($user)];
    }

    public function login(string $email, string $password): array
    {
        $user = $this->findByEmail($email);

        if (!$user || !password_verify($password, $user['password'] ?? '')) {
            return ['ok' => false, 'error' => 'Email ou senha invalido.'];
        }

        if (($user['status'] ?? '') !== 'active') {
            return ['ok' => false, 'error' => 'A sua conta ainda nao esta activa.'];
        }

        $this->updateUser((int) $user['id'], ['last_login_at' => date('c')]);
        $freshUser = $this->findById((int) $user['id']);

        session_regenerate_id(true);
        $_SESSION['user'] = $this->withoutPassword($freshUser ?? $user);

        $this->logs->log('login', 'Sessao iniciada.', (int) $user['id']);

        return ['ok' => true];
    }

    public function logout(): void
    {
        $user = current_user();

        if ($user) {
            $this->logs->log('logout', 'Sessao terminada.', (int) $user['id']);
        }

        unset($_SESSION['user']);
        session_regenerate_id(true);
    }

    public function updateProfile(int $userId, array $data): array
    {
        $user = $this->findById($userId);

        if (!$user) {
            return ['ok' => false, 'error' => 'Utilizador nao encontrado.'];
        }

        $name = trim($data['name'] ?? '');

        if ($name === '') {
            return ['ok' => false, 'error' => 'O nome e obrigatorio.'];
        }

        $profile = $user['profile'] ?? [];
        $profile['province'] = trim($data['province'] ?? '');
        $profile['city'] = trim($data['city'] ?? '');
        $profile['bio'] = trim($data['bio'] ?? '');

        $updated = $this->updateUser($userId, [
            'name' => $name,
            'phone' => trim($data['phone'] ?? ''),
            'profile' => $profile,
            'updated_at' => date('c'),
        ]);

        $_SESSION['user'] = $this->withoutPassword($updated);
        $this->logs->log('profile_updated', 'Perfil actualizado.', $userId);

        return ['ok' => true, 'user' => $this->withoutPassword($updated)];
    }

    public function users(array $filters = []): array
    {
        $users = array_map(fn (array $user) => $this->withoutPassword($user), $this->users->all());
        $role = $filters['role'] ?? '';
        $status = $filters['status'] ?? '';
        $search = strtolower(trim($filters['search'] ?? ''));

        return array_values(array_filter($users, function (array $user) use ($role, $status, $search): bool {
            if ($role !== '' && ($user['role'] ?? '') !== $role) {
                return false;
            }

            if ($status !== '' && ($user['status'] ?? '') !== $status) {
                return false;
            }

            if ($search !== '') {
                $haystack = strtolower(($user['name'] ?? '') . ' ' . ($user['email'] ?? '') . ' ' . ($user['phone'] ?? ''));

                if (!str_contains($haystack, $search)) {
                    return false;
                }
            }

            return true;
        }));
    }

    public function stats(): array
    {
        $stats = [
            'total' => 0,
            'students' => 0,
            'instructors' => 0,
            'institutions' => 0,
            'admins' => 0,
            'pending' => 0,
            'active' => 0,
            'suspended' => 0,
            'rejected' => 0,
        ];

        foreach ($this->users->all() as $user) {
            $stats['total']++;

            $role = $user['role'] ?? '';
            $status = $user['status'] ?? '';

            if ($role === 'student') {
                $stats['students']++;
            } elseif ($role === 'instructor') {
                $stats['instructors']++;
            } elseif ($role === 'institution') {
                $stats['institutions']++;
            } elseif ($role === 'admin') {
                $stats['admins']++;
            }

            if (isset($stats[$status])) {
                $stats[$status]++;
            }
        }

        return $stats;
    }

    public function changeStatus(int $userId, string $status, int $adminId): array
    {
        if (!in_array($status, ['active', 'pending', 'suspended', 'rejected'], true)) {
            return ['ok' => false, 'error' => 'Estado invalido.'];
        }

        $user = $this->findById($userId);

        if (!$user) {
            return ['ok' => false, 'error' => 'Utilizador nao encontrado.'];
        }

        if (($user['role'] ?? '') === 'admin' && $status !== 'active') {
            return ['ok' => false, 'error' => 'Conta admin nao pode ser bloqueada nesta fase.'];
        }

        $updated = $this->updateUser($userId, [
            'status' => $status,
            'approved_at' => $status === 'active' ? date('c') : ($user['approved_at'] ?? null),
            'approved_by' => $status === 'active' ? $adminId : ($user['approved_by'] ?? null),
            'updated_at' => date('c'),
        ]);

        $this->logs->log('user_status_changed', "Estado alterado para {$status}.", $adminId);

        return ['ok' => true, 'user' => $this->withoutPassword($updated)];
    }

    public function requestPasswordReset(string $email): array
    {
        $user = $this->findByEmail($email);

        if (!$user) {
            return ['ok' => true, 'token' => null];
        }

        $token = bin2hex(random_bytes(20));
        $this->updateUser((int) $user['id'], [
            'password_reset_token' => password_hash($token, PASSWORD_DEFAULT),
            'password_reset_requested_at' => date('c'),
        ]);

        $this->logs->log('password_reset_requested', 'Recuperacao de senha solicitada.', (int) $user['id']);

        return ['ok' => true, 'token' => $token];
    }

    public function resetPassword(string $email, string $token, string $password): array
    {
        $user = $this->findByEmail($email);

        if (!$user || !password_verify($token, $user['password_reset_token'] ?? '')) {
            return ['ok' => false, 'error' => 'Token invalido.'];
        }

        if (strlen($password) < 8) {
            return ['ok' => false, 'error' => 'A nova senha deve ter pelo menos 8 caracteres.'];
        }

        $this->updateUser((int) $user['id'], [
            'password' => password_hash($password, PASSWORD_DEFAULT),
            'password_reset_token' => null,
            'password_reset_requested_at' => null,
            'updated_at' => date('c'),
        ]);

        $this->logs->log('password_reset_completed', 'Senha recuperada.', (int) $user['id']);

        return ['ok' => true];
    }

    public function findByEmail(string $email): ?array
    {
        $email = strtolower(trim($email));

        foreach ($this->users->all() as $user) {
            if (($user['email'] ?? '') === $email) {
                return $user;
            }
        }

        return null;
    }

    public function findById(int $id): ?array
    {
        foreach ($this->users->all() as $user) {
            if ((int) ($user['id'] ?? 0) === $id) {
                return $user;
            }
        }

        return null;
    }

    private function updateUser(int $id, array $changes): array
    {
        $records = $this->users->all();
        $updated = [];

        foreach ($records as $index => $user) {
            if ((int) ($user['id'] ?? 0) === $id) {
                $records[$index] = array_merge($user, $changes);
                $updated = $records[$index];
                break;
            }
        }

        $this->users->save($records);

        return $updated;
    }

    private function validateRegistration(array $data): array
    {
        $errors = [];
        $role = $data['role'] ?? '';

        if (trim($data['name'] ?? '') === '') {
            $errors[] = 'O nome e obrigatorio.';
        }

        if (!filter_var($data['email'] ?? '', FILTER_VALIDATE_EMAIL)) {
            $errors[] = 'Informe um email valido.';
        } elseif ($this->findByEmail((string) $data['email'])) {
            $errors[] = 'Este email ja esta registado.';
        }

        if (!in_array($role, ['student', 'instructor', 'institution'], true)) {
            $errors[] = 'Escolha um tipo de conta valido.';
        }

        if (strlen($data['password'] ?? '') < 8) {
            $errors[] = 'A senha deve ter pelo menos 8 caracteres.';
        }

        if (($data['password'] ?? '') !== ($data['password_confirmation'] ?? '')) {
            $errors[] = 'A confirmacao da senha nao confere.';
        }

        return $errors;
    }

    private function withoutPassword(array $user): array
    {
        unset($user['password'], $user['password_reset_token']);

        return $user;
    }

    private function seedAdmin(): void
    {
        if ($this->users->all()) {
            return;
        }

        $this->users->insert([
            'name' => 'Administrador',
            'email' => 'admin@mozcursos.local',
            'phone' => '',
            'password' => password_hash('Admin12345', PASSWORD_DEFAULT),
            'role' => 'admin',
            'status' => 'active',
            'profile' => [
                'province' => '',
                'city' => '',
                'bio' => 'Conta inicial do sistema.',
            ],
            'password_reset_token' => null,
            'created_at' => date('c'),
            'updated_at' => date('c'),
            'last_login_at' => null,
        ]);
    }
}
