<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Concerns\WritesAuthLog;
use App\Http\Controllers\Controller;
use App\Http\Requests\Account\ChangePasswordRequest;
use App\Models\User;
use App\Services\UserSessionTracker;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

/**
 * Аккаунт пользователя — вкладка «Безопасность» (UI-аудит 21.06.2026).
 *
 * Заменяет статичные mock-карточки (ChangePasswordCard/SessionsTable):
 *  - POST   /api/account/change-password — реальная смена пароля.
 *  - GET    /api/account/security        — дата последней смены пароля + активные сессии.
 *  - DELETE /api/account/sessions/{id}    — отозвать сессию (UI-аудит 21.06.2026).
 *
 * Активные сессии берутся из user_sessions (запись при входе); отзыв реально
 * убивает сессию (удаление из Redis по session_id). Заменяет прежний mock.
 */
class AccountController extends Controller
{
    use WritesAuthLog;

    /**
     * POST /api/account/change-password — смена пароля авторизованным пользователем.
     *
     * Проверяет текущий пароль (Hash::check против password_hash), пишет новый хэш,
     * логирует password_changed в auth_log. На неверном текущем — 422 + лог
     * password_change_failed.
     */
    public function changePassword(ChangePasswordRequest $request): JsonResponse
    {
        /** @var User $user */
        $user = $request->user();

        if (! Hash::check($request->string('current_password')->toString(), (string) $user->password_hash)) {
            $this->logAuthEvent(
                'password_change_failed',
                $user->id,
                $user->tenant_id,
                $user->email,
                $request->ip(),
                $request->userAgent(),
                'wrong_current_password',
            );

            throw ValidationException::withMessages([
                'current_password' => ['Неверный текущий пароль.'],
            ]);
        }

        $user->forceFill([
            'password_hash' => Hash::make($request->string('password')->toString()),
        ])->save();

        $this->logAuthEvent(
            'password_changed',
            $user->id,
            $user->tenant_id,
            $user->email,
            $request->ip(),
            $request->userAgent(),
            null,
        );

        return response()->json([
            'message' => 'Пароль изменён.',
            'last_password_change_at' => now()->toIso8601String(),
        ]);
    }

    /**
     * GET /api/account/security — данные вкладки «Безопасность».
     *
     * last_password_change_at — max(created_at) по password-событиям в auth_log
     * (null, если пароль ни разу не менялся через портал).
     * recent_logins — последние входы текущего пользователя (устройство/IP/время).
     */
    public function security(Request $request): JsonResponse
    {
        /** @var User $user */
        $user = $request->user();

        $lastChange = DB::table('auth_log')
            ->where('user_id', $user->id)
            ->whereIn('event', ['password_changed', 'password_reset_completed'])
            ->max('created_at');

        $currentSid = $request->session()->getId();
        $rows = DB::table('user_sessions')
            ->where('user_id', $user->id)
            ->where('expires_at', '>', now())
            ->orderByDesc('created_at')
            ->limit(20)
            ->get(['id', 'token_hash', 'ip_address', 'user_agent', 'last_active_at', 'created_at']);

        $sessions = $rows->map(fn ($row): array => [
            'id' => $row->id,
            'device' => $this->deviceLabel($row->user_agent),
            'ip' => $row->ip_address,
            'at' => Carbon::parse($row->last_active_at ?? $row->created_at)->toIso8601String(),
            'current' => $row->token_hash === $currentSid,
        ])->all();

        return response()->json([
            'last_password_change_at' => $lastChange ? Carbon::parse($lastChange)->toIso8601String() : null,
            'sessions' => $sessions,
        ]);
    }

    /** DELETE /api/account/sessions/{id} — отозвать конкретную сессию пользователя. */
    public function revokeSession(Request $request, int $id): JsonResponse
    {
        /** @var User $user */
        $user = $request->user();
        $ok = app(UserSessionTracker::class)->revoke($user->id, $id);

        if (! $ok) {
            return response()->json(['message' => 'Сессия не найдена.'], 404);
        }

        $this->logAuthEvent(
            'session_revoked',
            $user->id,
            $user->tenant_id,
            $user->email,
            $request->ip(),
            $request->userAgent(),
            null,
        );

        return response()->json(['message' => 'Сессия завершена.']);
    }

    /** Грубый человекочитаемый ярлык устройства из User-Agent (браузер + ОС). */
    private function deviceLabel(?string $ua): string
    {
        if ($ua === null || $ua === '') {
            return 'Неизвестное устройство';
        }

        $browser = match (true) {
            str_contains($ua, 'Firefox/') => 'Firefox',
            str_contains($ua, 'Edg/') => 'Edge',
            str_contains($ua, 'OPR/') || str_contains($ua, 'Opera') => 'Opera',
            str_contains($ua, 'Chrome/') => 'Chrome',
            str_contains($ua, 'Safari/') => 'Safari',
            default => 'Браузер',
        };

        $os = match (true) {
            str_contains($ua, 'Windows') => 'Windows',
            str_contains($ua, 'Android') => 'Android',
            str_contains($ua, 'iPhone') || str_contains($ua, 'iPad') => 'iOS',
            str_contains($ua, 'Mac OS') || str_contains($ua, 'Macintosh') => 'macOS',
            str_contains($ua, 'Linux') => 'Linux',
            default => '',
        };

        return $os !== '' ? "{$browser}, {$os}" : $browser;
    }
}
