<?php

declare(strict_types=1);

namespace SHServ\Controllers;

use GNexus\GAuth\DTO\TokenSet;
use SHServ\Integrations\GAuth\AuthControllerTrait;
use SHServ\Integrations\GAuth\AuthService;
use SHServ\Integrations\GAuth\PermissionResolver;
use SHServ\Integrations\GAuth\Store\DbTokenStore;
use SHServ\Integrations\GAuth\UserResolver;

class AuthController extends \SHServ\Middleware\Controller
{
    use AuthControllerTrait;

    private function logAuth(string $msg): void
    {
        $path = dirname(__DIR__, 2) . '/Logs/gauth_debug.log';
        @file_put_contents($path, date('Y-m-d H:i:s') . ' ' . $msg . "\n", FILE_APPEND | LOCK_EX);
    }

    /**
     * GET /auth/login
     * Redirect user to gnexus-auth authorization page.
     */
    public function login()
    {
        $this->logAuth('login() called');
        $service = new AuthService();
        $returnTo = $_GET['return_to'] ?? '/';
        $url = $service->buildLoginUrl($returnTo);
        $this->logAuth("login() redirect to: {$url}");
        return $this->utils()->redirect($url);
    }

    /**
     * GET /auth/callback
     * Handle OAuth callback from gnexus-auth.
     */
    public function callback()
    {
        if (session_status() === PHP_SESSION_NONE) {
            @session_start();
        }

        $this->logAuth('callback() called');
        try {
            $code = isset($_GET['code']) ? (string) $_GET['code'] : '';
            $state = isset($_GET['state']) ? (string) $_GET['state'] : '';

            $this->logAuth("callback() code={$code}, state={$state}");

            if ($code === '' || $state === '') {
                $this->logAuth('callback() empty code/state');
                return $this->utils()->response_error('invalid_callback');
            }

            $service = new AuthService();
            $this->logAuth('callback() AuthService created');

            try {
                $user = $service->handleCallback($code, $state);
                $this->logAuth('callback() handleCallback OK, userId=' . $user->userId);
            } catch (\GNexus\GAuth\Exception\GAuthException $e) {
                $this->logAuth('callback() GAuthException: ' . $e->getMessage());
                return $this->utils()->response_error('auth_failed', [], ['message' => $e->getMessage()]);
            } catch (\Throwable $e) {
                $this->logAuth('callback() Throwable in handleCallback: ' . get_class($e) . ' - ' . $e->getMessage());
                throw $e;
            }

            $resolver = new UserResolver();
            $this->logAuth('callback() UserResolver created');

            try {
                $localUserId = $resolver->resolve($user);
                $this->logAuth('callback() resolve OK, localUserId=' . $localUserId);
            } catch (\Throwable $e) {
                $this->logAuth('callback() Throwable in resolve: ' . get_class($e) . ' - ' . $e->getMessage());
                throw $e;
            }

            $_SESSION['shserv_user_id'] = $localUserId;

            // Persist tokens to DB now that we have the local user ID
            $sessionToken = $_SESSION['shserv_auth_token'] ?? null;
            $tokenData = $_SESSION['shserv_last_token_set'] ?? null;
            if ($sessionToken && $tokenData) {
                $expiresAt = $tokenData['expires_at'] ? new \DateTimeImmutable($tokenData['expires_at']) : null;
                $tokenSet = new TokenSet(
                    accessToken: $tokenData['access_token'],
                    refreshToken: $tokenData['refresh_token'] ?? null,
                    tokenType: $tokenData['token_type'] ?? 'Bearer',
                    expiresIn: $expiresAt ? (int) $expiresAt->format('U') - time() : 0,
                    expiresAt: $expiresAt,
                );
                $dbStore = new DbTokenStore(app()->thin_builder);
                $dbStore->put($sessionToken, $tokenSet, $localUserId);
                unset($_SESSION['shserv_last_token_set']);
                $this->logAuth('callback() DbTokenStore->put() OK');
            }

            // Redirect back to app
            $context = $_SESSION['gauth_state'][$state]['context'] ?? [];
            $returnTo = $context['return_to'] ?? '/';
            $this->logAuth("callback() redirect to: {$returnTo}");
            return $this->utils()->redirect($returnTo);
        } catch (\Throwable $e) {
            $this->logAuth('callback() UNCAUGHT Throwable: ' . get_class($e) . ' - ' . $e->getMessage());
            $this->logAuth('callback() Stack: ' . str_replace("\n", ' | ', $e->getTraceAsString()));
            throw $e;
        }
    }

    /**
     * POST /auth/logout
     */
    public function logout()
    {
        $service = new AuthService();
        $service->logout();
        return $this->utils()->response_success();
    }

    /**
     * GET /auth/me
     * Return current authenticated user + effective permissions.
     */
    public function me()
    {
        $user = $this->get_current_user();
        if (!$user) {
            return $this->utils()->response_error('not_found_any_sessions', [], [], 401);
        }

        $permissions = $this->get_current_permissions();

        return $this->utils()->response_success([
            'user' => [
                'id' => $user['id'],
                'gauth_user_id' => $user['gauth_user_id'],
                'email' => $user['email'],
                'display_name' => $user['display_name'],
                'avatar_url' => $user['avatar_url'],
                'system_role' => $user['system_role'],
                'status' => $user['status'],
            ],
            'permissions' => $permissions,
        ]);
    }

    /**
     * POST /auth/refresh
     * Refresh access token using stored refresh token.
     */
    public function refresh()
    {
        $sessionToken = $_SESSION['shserv_auth_token'] ?? null;
        if (!$sessionToken) {
            return $this->utils()->response_error('not_found_any_sessions', [], [], 401);
        }

        $service = new AuthService();
        $tokenSet = $service->refreshAccessToken($sessionToken);

        if (!$tokenSet) {
            return $this->utils()->response_error('session_expired', [], [], 401);
        }

        return $this->utils()->response_success([
            'access_token' => $tokenSet->accessToken,
            'expires_in' => $tokenSet->expiresIn,
        ]);
    }
}
