<?php

namespace App\Support\Workspaces;

use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use Illuminate\Http\Request;

final class WorkspaceContext
{
    public const SESSION_KEY = 'current_workspace_id';

    public const INTENDED_URL_SESSION_KEY = 'workspace_intended_url';

    public const LAST_TENANT_IDS_SESSION_KEY = 'workspace_last_tenant_ids';

    public function __construct(private WorkspaceResolver $resolver) {}

    public function currentWorkspaceId(?Request $request = null): ?int
    {
        $session = ($request && $request->hasSession()) ? $request->session() : session();

        $id = $session->get(self::SESSION_KEY);

        return is_int($id) ? $id : (is_numeric($id) ? (int) $id : null);
    }

    public function currentWorkspace(?Request $request = null): ?Workspace
    {
        $id = $this->currentWorkspaceId($request);

        if (! $id) {
            return null;
        }

        $workspace = Workspace::query()->whereKey($id)->first();

        if (! $workspace) {
            return null;
        }

        if (! $this->isWorkspaceSelectable($workspace)) {
            return null;
        }

        return $workspace;
    }

    public function setCurrentWorkspace(Workspace $workspace, ?User $user = null, ?Request $request = null): void
    {
        $session = ($request && $request->hasSession()) ? $request->session() : session();
        $session->put(self::SESSION_KEY, (int) $workspace->getKey());

        if ($user !== null) {
            $user->forceFill(['last_workspace_id' => (int) $workspace->getKey()])->save();
        }
    }

    public function rememberLastTenantId(int $workspaceId, int $tenantId, ?Request $request = null): void
    {
        $session = ($request && $request->hasSession()) ? $request->session() : session();

        $map = $session->get(self::LAST_TENANT_IDS_SESSION_KEY, []);
        $map = is_array($map) ? $map : [];

        $map[(string) $workspaceId] = $tenantId;

        $session->put(self::LAST_TENANT_IDS_SESSION_KEY, $map);
    }

    public function lastTenantId(?Request $request = null): ?int
    {
        $workspaceId = $this->currentWorkspaceId($request);

        if ($workspaceId === null) {
            return null;
        }

        $session = ($request && $request->hasSession()) ? $request->session() : session();

        $map = $session->get(self::LAST_TENANT_IDS_SESSION_KEY, []);
        $map = is_array($map) ? $map : [];

        $id = $map[(string) $workspaceId] ?? null;

        return is_int($id) ? $id : (is_numeric($id) ? (int) $id : null);
    }

    public function clearLastTenantId(?Request $request = null): void
    {
        $workspaceId = $this->currentWorkspaceId($request);

        if ($workspaceId === null) {
            return;
        }

        $session = ($request && $request->hasSession()) ? $request->session() : session();

        $map = $session->get(self::LAST_TENANT_IDS_SESSION_KEY, []);
        $map = is_array($map) ? $map : [];

        unset($map[(string) $workspaceId]);

        $session->put(self::LAST_TENANT_IDS_SESSION_KEY, $map);
    }

    public function clearCurrentWorkspace(?User $user = null, ?Request $request = null): void
    {
        $session = ($request && $request->hasSession()) ? $request->session() : session();
        $session->forget(self::SESSION_KEY);

        if ($user !== null && $user->last_workspace_id !== null) {
            $user->forceFill(['last_workspace_id' => null])->save();
        }
    }

    public function resolveInitialWorkspaceFor(User $user, ?Request $request = null): ?Workspace
    {
        $session = ($request && $request->hasSession()) ? $request->session() : session();

        $currentId = $this->currentWorkspaceId($request);

        if ($currentId) {
            $current = Workspace::query()->whereKey($currentId)->first();

            if (! $current instanceof Workspace || ! $this->isWorkspaceSelectable($current) || ! $this->isMember($user, $current)) {
                $session->forget(self::SESSION_KEY);

                if ((int) $user->last_workspace_id === (int) $currentId) {
                    $user->forceFill(['last_workspace_id' => null])->save();
                }
            } else {
                return $current;
            }
        }

        if ($user->last_workspace_id !== null) {
            $workspace = Workspace::query()->whereKey($user->last_workspace_id)->first();

            if (! $workspace instanceof Workspace || ! $this->isWorkspaceSelectable($workspace) || ! $this->isMember($user, $workspace)) {
                $user->forceFill(['last_workspace_id' => null])->save();
            }
        }

        $memberships = WorkspaceMembership::query()
            ->where('user_id', $user->getKey())
            ->with('workspace')
            ->get();

        $selectableWorkspaces = $memberships
            ->map(fn (WorkspaceMembership $membership) => $membership->workspace)
            ->filter(fn (?Workspace $workspace) => $workspace instanceof Workspace && $this->isWorkspaceSelectable($workspace))
            ->values();

        if ($selectableWorkspaces->count() === 1) {
            /** @var Workspace $workspace */
            $workspace = $selectableWorkspaces->first();

            $session->put(self::SESSION_KEY, (int) $workspace->getKey());
            $user->forceFill(['last_workspace_id' => (int) $workspace->getKey()])->save();

            return $workspace;
        }

        return null;
    }

    public function isMember(User $user, Workspace $workspace): bool
    {
        return WorkspaceMembership::query()
            ->where('user_id', $user->getKey())
            ->where('workspace_id', $workspace->getKey())
            ->exists();
    }

    private function isWorkspaceSelectable(Workspace $workspace): bool
    {
        return empty($workspace->archived_at);
    }
}
