<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

/**
 * Handles the OAuth2 "Authenticate with Office365" flow for the Microsoft
 * Outlook / Office365 SMTP connection. Mirrors GoogleOAuthController's
 * session-staging + popup pattern - see that class for the rationale.
 */
class MicrosoftOAuthController extends Controller
{
    private const TENANT = 'common';
    private const SCOPES = 'offline_access https://graph.microsoft.com/Mail.Send https://graph.microsoft.com/User.Read';

    public function start(Request $request)
    {
        $data = $request->validate([
            'client_id'     => 'required|string|max:255',
            'client_secret' => 'required|string|max:500',
        ]);

        $state = Str::random(40);

        session(["microsoft_oauth.{$state}" => [
            'client_id'     => $data['client_id'],
            'client_secret' => $data['client_secret'],
            'user_id'       => $request->user()->id,
            'created_at'    => now()->timestamp,
        ]]);

        $query = http_build_query([
            'client_id'     => $data['client_id'],
            'redirect_uri'  => route('smtp.microsoft.callback'),
            'response_type' => 'code',
            'response_mode' => 'query',
            'prompt'        => 'select_account',
            'scope'         => self::SCOPES,
            'state'         => $state,
        ]);

        return response()->json([
            'url' => 'https://login.microsoftonline.com/' . self::TENANT . "/oauth2/v2.0/authorize?{$query}",
        ]);
    }

    public function callback(Request $request)
    {
        $state = (string) $request->query('state');
        $code  = $request->query('code');
        $error = $request->query('error');

        $key     = "microsoft_oauth.{$state}";
        $pending = $state !== '' ? session($key) : null;
        session()->forget($key);

        if ($error || !$pending || !$code || $pending['user_id'] !== $request->user()?->id
            || (now()->timestamp - $pending['created_at']) > 900) {
            return view('microsoft-oauth-result', [
                'success' => false,
                'message' => $error
                    ? ($request->query('error_description') ?: "Microsoft denied access: {$error}")
                    : 'This authentication link has expired or is invalid - please try again.',
            ]);
        }

        try {
            $tokenResponse = Http::asForm()->post('https://login.microsoftonline.com/' . self::TENANT . '/oauth2/v2.0/token', [
                'client_id'     => $pending['client_id'],
                'client_secret' => $pending['client_secret'],
                'code'          => $code,
                'redirect_uri'  => route('smtp.microsoft.callback'),
                'grant_type'    => 'authorization_code',
                'scope'         => self::SCOPES,
            ]);

            $tokens = $tokenResponse->json() ?? [];

            if (!$tokenResponse->successful() || empty($tokens['access_token'])) {
                throw new \RuntimeException($tokens['error_description'] ?? $tokens['error'] ?? 'Microsoft did not return an access token.');
            }

            $me = Http::withToken($tokens['access_token'])
                ->get('https://graph.microsoft.com/v1.0/me')
                ->json() ?? [];

            return view('microsoft-oauth-result', [
                'success'      => true,
                'refreshToken' => $tokens['refresh_token'] ?? null,
                'accessToken'  => $tokens['access_token'],
                'expiresAt'    => now()->addSeconds((int) ($tokens['expires_in'] ?? 3600))->toISOString(),
                'accountEmail' => $me['mail'] ?? $me['userPrincipalName'] ?? null,
            ]);
        } catch (\Throwable $e) {
            return view('microsoft-oauth-result', [
                'success' => false,
                'message' => $e->getMessage(),
            ]);
        }
    }
}
