<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\{AuditLog, Company, User, UserCompanyGroupPreference};
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class SelectCompanyController extends Controller
{
    public function show(Request $request)
    {
        $context = $this->preferenceContext($request);

        if (!$context['username']) {
            return redirect()->route('login');
        }

        $allCompanies = $this->resolveSelectableCompanies($request);

        if ($allCompanies->isEmpty()) {
            return redirect()->route('login')
                ->withErrors(['username' => 'No company workspace available. Please contact support.']);
        }

        if ($this->shouldAutoEnterWorkspace($allCompanies, $context) && !$request->user()) {
            $visible = $this->filterVisibleCompanies($allCompanies, $context);

            if ($visible->count() === 1) {
                return $this->completeLogin(
                    $request,
                    $visible->first()['user_id'],
                    $request->session()->get('pending_login.remember', false)
                );
            }
        }

        $hiddenIds = UserCompanyGroupPreference::hiddenIds($context['username'], $context['company_group_id']);
        $visible = $this->filterVisibleCompanies($allCompanies, $context);
        $hidden = $allCompanies->filter(fn ($row) => in_array($row['company_id'], $hiddenIds, true))->values();

        $sort = $request->query('sort', 'asc') === 'desc' ? 'desc' : 'asc';
        $sorter = $sort === 'desc'
            ? fn ($a, $b) => strcasecmp($b['name'], $a['name'])
            : fn ($a, $b) => strcasecmp($a['name'], $b['name']);

        $visible = $visible->sort($sorter)->values();
        $hidden = $hidden->sort($sorter)->values();

        $lastUsedMap = $this->lastUsedDatesForCompanies($allCompanies->pluck('company_id')->unique());
        $visible = $this->attachLastUsedDates($visible, $lastUsedMap);
        $hidden = $this->attachLastUsedDates($hidden, $lastUsedMap);

        $grouped = $this->groupCompaniesForDisplay($visible);
        $groupWelcome = $grouped->first(
            fn ($group) => !empty($group['group_name']) && !empty($group['group_code'])
        );
        $currentCompanyId = auth()->user()?->company_id;
        $showAddPanel = $request->boolean('add');

        return view('auth.select-company', compact(
            'grouped',
            'groupWelcome',
            'hidden',
            'currentCompanyId',
            'sort',
            'showAddPanel',
            'context'
        ));
    }

    public function select(Request $request)
    {
        $request->validate(['company_id' => 'required|integer']);

        $companies = $this->resolveSelectableCompanies($request);
        $match = $companies->firstWhere('company_id', (int) $request->company_id);

        if (!$match) {
            return back()->withErrors(['company_id' => 'You do not have access to that company.']);
        }

        if (auth()->check()) {
            return $this->switchCompany($request, (int) $request->company_id, $match['user_id']);
        }

        return $this->completeLogin(
            $request,
            $match['user_id'],
            $request->session()->get('pending_login.remember', false)
        );
    }

    public function hide(Request $request)
    {
        $request->validate(['company_id' => 'required|integer']);

        $context = $this->preferenceContext($request);
        $companies = $this->resolveSelectableCompanies($request);

        if (!$companies->contains('company_id', (int) $request->company_id)) {
            return back()->withErrors(['company_id' => 'Invalid company.']);
        }

        UserCompanyGroupPreference::hide(
            $context['username'],
            $context['company_group_id'],
            (int) $request->company_id
        );

        return redirect()->route('select-company', ['sort' => $request->query('sort', 'asc')])
            ->with('success', 'Company hidden from your list. Use Add to show it again.');
    }

    public function unhide(Request $request)
    {
        $request->validate(['company_id' => 'required|integer']);

        $context = $this->preferenceContext($request);

        UserCompanyGroupPreference::unhide(
            $context['username'],
            $context['company_group_id'],
            (int) $request->company_id
        );

        return redirect()->route('select-company', [
            'sort' => $request->query('sort', 'asc'),
            'add'  => 1,
        ])->with('success', 'Company added back to your list.');
    }

    public function skip(Request $request)
    {
        if (auth()->check()) {
            return redirect()->route('dashboard');
        }

        $context = $this->preferenceContext($request);
        $companies = $this->filterVisibleCompanies(
            $this->resolveSelectableCompanies($request),
            $context
        );
        $first = $companies->first() ?? $this->resolveSelectableCompanies($request)->first();

        if (!$first) {
            return redirect()->route('login');
        }

        return $this->completeLogin(
            $request,
            $first['user_id'],
            $request->session()->get('pending_login.remember', false)
        );
    }

    private function preferenceContext(Request $request): array
    {
        if (auth()->check()) {
            $user = auth()->user()->load('company');

            return [
                'username'         => $user->username,
                'company_group_id' => $user->company->company_group_id,
            ];
        }

        $userIds = $request->session()->get('pending_login.user_ids', []);
        $user = User::withoutGlobalScopes()
            ->with('company')
            ->whereIn('id', $userIds)
            ->first();

        return [
            'username'         => $user?->username,
            'company_group_id' => $user?->company?->company_group_id,
        ];
    }

    private function filterVisibleCompanies($companies, array $context)
    {
        $hiddenIds = UserCompanyGroupPreference::hiddenIds(
            $context['username'],
            $context['company_group_id']
        );

        return $companies
            ->reject(fn ($row) => in_array($row['company_id'], $hiddenIds, true))
            ->values();
    }

    private function shouldAutoEnterWorkspace($companies, array $context): bool
    {
        if ($context['company_group_id'] === null) {
            return $companies->count() === 1;
        }

        return $companies->count() <= 1;
    }

    private function resolveSelectableCompanies(Request $request)
    {
        if ($request->session()->has('pending_login.user_ids')) {
            return $this->companiesFromUserIds($request->session()->get('pending_login.user_ids', []));
        }

        if (auth()->check()) {
            return $this->companiesForAuthenticatedUser(auth()->user());
        }

        return collect();
    }

    private function companiesFromUserIds(array $userIds)
    {
        $users = User::withoutGlobalScopes()
            ->with(['company.group', 'company.subscription'])
            ->whereIn('id', $userIds)
            ->where('is_active', true)
            ->whereNull('deleted_at')
            ->get();

        if ($users->isEmpty()) {
            return collect();
        }

        $username = $users->first()->username;
        $groupIds = $users->pluck('company.company_group_id')->filter()->unique();

        if ($groupIds->isNotEmpty()) {
            $companyIds = Company::whereIn('company_group_id', $groupIds)
                ->whereIn('status', ['active', 'trial'])
                ->pluck('id');

            $users = User::withoutGlobalScopes()
                ->with(['company.group', 'company.subscription'])
                ->whereIn('company_id', $companyIds)
                ->where('username', $username)
                ->where('is_active', true)
                ->whereNull('deleted_at')
                ->get();
        }

        return $users
            ->filter(fn (User $user) => $this->companyIsAccessible($user->company))
            ->map(fn (User $user) => $this->companyRow($user))
            ->unique('company_id')
            ->values();
    }

    private function companiesForAuthenticatedUser(User $user)
    {
        $group = $user->company->group;

        if (!$group) {
            return collect([$this->companyRow($user)]);
        }

        $companyIds = $group->companies()
            ->whereIn('status', ['active', 'trial'])
            ->pluck('id');

        return User::withoutGlobalScopes()
            ->with(['company.group', 'company.subscription'])
            ->whereIn('company_id', $companyIds)
            ->where('username', $user->username)
            ->where('is_active', true)
            ->whereNull('deleted_at')
            ->get()
            ->map(fn (User $u) => $this->companyRow($u))
            ->unique('company_id')
            ->values();
    }

    private function companyRow(User $user): array
    {
        $company = $user->company;

        return [
            'user_id'       => $user->id,
            'company_id'    => $company->id,
            'name'          => $company->name,
            'company_code'  => $company->company_code,
            'logo_url'      => $company->logo_url,
            'has_logo'      => $company->hasLogo(),
            'group_id'      => $company->company_group_id,
            'group_code'    => $company->group?->group_code,
            'group_name'    => $company->group?->name,
        ];
    }

    private function lastUsedDatesForCompanies($companyIds): array
    {
        $ids = collect($companyIds)->filter()->unique()->values();

        if ($ids->isEmpty()) {
            return [];
        }

        return User::withoutGlobalScopes()
            ->whereIn('company_id', $ids)
            ->where('is_active', true)
            ->whereNull('deleted_at')
            ->whereNotNull('last_login_at')
            ->groupBy('company_id')
            ->selectRaw('company_id, MAX(last_login_at) as last_used_at')
            ->pluck('last_used_at', 'company_id')
            ->map(fn ($date) => \Carbon\Carbon::parse($date))
            ->all();
    }

    private function attachLastUsedDates($companies, array $lastUsedMap)
    {
        return $companies->map(function (array $row) use ($lastUsedMap) {
            $row['last_used_at'] = $lastUsedMap[$row['company_id']] ?? null;

            return $row;
        });
    }

    private function groupCompaniesForDisplay($companies)
    {
        $grouped = $companies->groupBy(fn ($row) => $row['group_id'] ?: 'ungrouped');

        return $grouped->map(function ($items, $key) {
            if ($key === 'ungrouped') {
                return [
                    'group_code' => null,
                    'group_name' => null,
                    'companies'  => $items->values(),
                ];
            }

            $first = $items->first();

            return [
                'group_code' => $first['group_code'],
                'group_name' => $first['group_name'],
                'companies'  => $items->values(),
            ];
        })->sortBy(fn ($g) => $g['group_name'] ?? 'zzz')->values();
    }

    private function completeLogin(Request $request, int $userId, bool $remember)
    {
        $user = User::withoutGlobalScopes()
            ->with('company.subscription')
            ->where('id', $userId)
            ->where('is_active', true)
            ->whereNull('deleted_at')
            ->firstOrFail();

        $company = $user->company;

        if (!$this->companyIsAccessible($company)) {
            return redirect()->route('login')
                ->withErrors(['username' => 'This company is not available.']);
        }

        $subscription = $company->subscription;
        if ($subscription && !$subscription->canLogin()) {
            return redirect()->route('login')->withErrors([
                'username' => "Maximum concurrent users ({$subscription->max_concurrent_users}) reached.",
            ]);
        }

        Auth::guard('web')->login($user, $remember);
        $request->session()->forget('pending_login');
        $request->session()->regenerate();

        $user->updateQuietly([
            'last_login_at' => now(),
            'last_login_ip' => $request->ip(),
        ]);

        AuditLog::record('login', 'auth', (string) $user->id, null, null,
            "User login: {$user->username} [{$company->name}]");

        if ($user->force_password_change) {
            return redirect()->route('password.change');
        }

        return redirect()->intended(route('dashboard'));
    }

    private function switchCompany(Request $request, int $companyId, int $userId)
    {
        $user = User::withoutGlobalScopes()
            ->with('company.subscription')
            ->where('id', $userId)
            ->where('company_id', $companyId)
            ->where('is_active', true)
            ->whereNull('deleted_at')
            ->firstOrFail();

        $company = $user->company;

        if (!$this->companyIsAccessible($company)) {
            return back()->withErrors(['company_id' => 'This company is not available.']);
        }

        Auth::guard('web')->login($user, $request->session()->get('remember_web', false));
        $request->session()->regenerate();

        $user->updateQuietly([
            'last_login_at' => now(),
            'last_login_ip' => $request->ip(),
        ]);

        AuditLog::record('company_switch', 'auth', (string) $user->id, null,
            ['company' => $company->name],
            "Switched workspace to {$company->name}");

        return redirect()->route('dashboard');
    }

    private function companyIsAccessible(?Company $company): bool
    {
        if (!$company) {
            return false;
        }

        return in_array($company->status, ['active', 'trial'], true);
    }

    public static function storePendingLogin(Request $request, $users, bool $remember): void
    {
        $request->session()->put('pending_login', [
            'user_ids' => $users->pluck('id')->all(),
            'remember' => $remember,
        ]);
    }

    public static function shouldShowCompanyPicker(Company $company, User $user): bool
    {
        $group = $company->group;

        if (!$group) {
            return false;
        }

        $accessible = User::withoutGlobalScopes()
            ->where('username', $user->username)
            ->whereIn('company_id', $group->companies()->pluck('id'))
            ->where('is_active', true)
            ->whereNull('deleted_at')
            ->get()
            ->filter(fn (User $u) => in_array($u->company->status, ['active', 'trial'], true));

        return $accessible->count() > 1;
    }
}
