<?php

namespace App\Services\Auth;

use App\Models\Configuration;
use App\Services\ApplicationUrlService;
use App\Models\LoginActivity;
use App\Models\Preferences;
use App\Models\Role;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

/**
 * Session-auth flow: session keys, dashboards, redirects, and preferences.
 */
class AuthSessionService
{
    private const FALLBACK_DASHBOARD = '/landing_page/guest_dashboard';

    public function __construct(
        private ApplicationUrlService $urls,
    ) {
    }

    /**
     * Sanitizes login redirects to same-host relative paths only.
     */
    public function sanitizeRedirectTarget(string $redirectTo): ?string
    {
        $redirectTo = trim($redirectTo);
        if ($redirectTo === '') {
            return null;
        }

        if (preg_match('#^https?://#i', $redirectTo)) {
            $appHost = (string) parse_url($this->urls->docsHomeUrl(), PHP_URL_HOST);
            $targetHost = (string) parse_url($redirectTo, PHP_URL_HOST);
            $targetPath = (string) parse_url($redirectTo, PHP_URL_PATH);
            $targetQuery = (string) parse_url($redirectTo, PHP_URL_QUERY);

            if ($appHost === '' || $targetHost === '' || strcasecmp($appHost, $targetHost) !== 0) {
                return null;
            }

            $redirectTo = $targetPath !== '' ? $targetPath : '/';
            if ($targetQuery !== '') {
                $redirectTo .= '?'.$targetQuery;
            }
        }

        if (! str_starts_with($redirectTo, '/')) {
            $redirectTo = '/'.ltrim($redirectTo, '/');
        }

        if (str_starts_with($redirectTo, '//')) {
            return null;
        }

        return $redirectTo;
    }

    /**
     * Highest-priority dashboard route matching any of the role names/slugs.
     *
     * @param  list<string>  $roleNames
     */
    public function dashboardRouteForRoles(array $roleNames): string
    {
        if ($roleNames === []) {
            return self::FALLBACK_DASHBOARD;
        }

        $rows = Role::findByNamesOrSlugs($roleNames);
        if ($rows !== []) {
            $route = $rows[0]->dashboard_route ?? null;
            if (is_string($route) && $route !== '') {
                return $route;
            }
        }

        return self::FALLBACK_DASHBOARD;
    }

    /**
     * Apply stored style preferences into the session.
     */
    public function applyStylePreferencesToSession(int $userId): void
    {
        if ($userId <= 0) {
            return;
        }

        $prefs = Preferences::query()->where('user_id', $userId)->first();
        if (! $prefs) {
            return;
        }

        $styleColor = (string) ($prefs->style_color ?? '');
        if ($styleColor !== '') {
            session()->put('style_color', $styleColor);
        }

        $menuColor = (string) ($prefs->menu_color ?? '');
        if ($menuColor === 'custom') {
            session()->put('menu_color', 'custom');
            session()->put('menu_custom_bg', (string) ($prefs->menu_custom_bg ?? '#0f172a'));
            session()->put('menu_custom_text', (string) ($prefs->menu_custom_text ?? '#ffffff'));
            session()->put('menu_custom_mode', (string) ($prefs->menu_custom_mode ?? 'dark'));
        } elseif ($menuColor !== '') {
            session()->put('menu_color', $menuColor);
        }
    }

    /**
     * Populate Laravel web auth and session state for the web portal.
     *
     * @param  list<string>  $roleNames
     */
    public function writeAuthenticatedSession(User $user, array $roleNames): void
    {
        Auth::guard('web')->login($user);

        $schoolYear = Configuration::getConfig('school_year');
        $semester = Configuration::getConfig('semester');
        $teacherContext = $user->teacherSessionContext();

        session([
            'user_id' => $user->id,
            'user_email' => $user->email,
            'user_name' => trim(($user->firstname ?? '').' '.($user->lastname ?? '')),
            'user_type' => $user->user_type ?? null,
            'is_logged_in' => true,
            'login_time' => time(),
            'last_activity' => time(),
            'roles' => $roleNames,
            'semester' => $semester,
            'school_year' => $schoolYear,
            'class_section_id' => $teacherContext['class_section_id'],
            'class_section_name' => $teacherContext['class_section_name'],
        ]);

        $this->applyStylePreferencesToSession((int) $user->id);
    }

    /**
     * Close open login_activity rows without logout_time for this user.
     */
    public function logLogout(?int $userId, ?string $email): void
    {
        if (! $userId || ! $email) {
            return;
        }

        LoginActivity::query()
            ->where('user_id', $userId)
            ->whereNull('logout_time')
            ->update([
                'logout_time' => utc_now(),
                'email' => $email,
            ]);
    }

    /**
     * Determine next navigation after credentials validated (single vs multi-role).
     *
     * @return array{kind: string, redirect_url?: string|null, roles?: array<int, string>, reason?: string}
     */
    public function resolvePostLoginDestination(User $user, ?string $sanitizedRedirect): array
    {
        $roleNames = $user->roleNames();

        if ($roleNames === []) {
            Log::notice('session_login: user has no roles', ['user_id' => $user->id]);

            return [
                'kind' => 'no_roles',
                'reason' => 'Role not assigned. Please contact support.',
            ];
        }

        $this->writeAuthenticatedSession($user, $roleNames);

        if (count($roleNames) === 1) {
            session()->put('role', $roleNames[0]);
            if ($sanitizedRedirect !== null) {
                return ['kind' => 'redirect', 'redirect_url' => $sanitizedRedirect];
            }

            return [
                'kind' => 'redirect',
                'redirect_url' => $this->dashboardRouteForRoles([$roleNames[0]]),
            ];
        }

        if ($sanitizedRedirect !== null) {
            session()->put('post_login_redirect', $sanitizedRedirect);
        } else {
            session()->forget('post_login_redirect');
        }

        return [
            'kind' => 'select_role',
            'roles' => array_values($roleNames),
            'redirect_url' => $this->urls->webSelectRoleUrl(false),
        ];
    }
}
