<?php

namespace App\Http\Controllers;

use App\Models\SmtpConfig;
use App\Services\SmtpConfigService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Inertia\Inertia;

class SmtpConfigController extends Controller
{
    /** Provider slugs that are implemented. More arrive one by one. */
    private const ACTIVE_PROVIDERS = ['smtp', 'ses', 'mailgun', 'sendgrid', 'brevo', 'sparkpost', 'netcore', 'postmark', 'elastic', 'smtp2go', 'tosend', 'phpmail', 'gmail', 'outlook'];

    /** Providers whose only credential is a single API key (password column). */
    private const API_KEY_PROVIDERS = ['sendgrid', 'brevo', 'sparkpost', 'netcore', 'postmark', 'elastic', 'smtp2go', 'tosend'];

    /** Amazon SES regions offered in the UI. */
    private const SES_REGIONS = [
        'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2',
        'af-south-1', 'ap-south-1', 'ap-northeast-1', 'ap-northeast-2',
        'ap-northeast-3', 'ap-southeast-1', 'ap-southeast-2', 'ca-central-1',
        'eu-central-1', 'eu-west-1', 'eu-west-2', 'eu-west-3',
        'eu-north-1', 'eu-south-1', 'me-south-1', 'sa-east-1',
    ];

    public function index()
    {
        $configs = SmtpConfig::orderByDesc('is_default')
            ->orderBy('name')
            ->get()
            ->map(fn($c) => [
                'id'                => $c->id,
                'provider'          => $c->provider,
                'name'              => $c->name,
                'host'              => $c->host,
                'port'              => $c->port,
                'encryption'        => $c->encryption,
                'auto_tls'          => $c->auto_tls,
                'auth_enabled'      => $c->auth_enabled,
                'username'          => $c->username,
                'region'            => $c->settings['region'] ?? null,
                'domain'            => $c->settings['domain'] ?? null,
                'message_stream'    => $c->settings['message_stream'] ?? null,
                'track_opens'       => $c->settings['track_opens'] ?? null,
                'track_links'       => $c->settings['track_links'] ?? null,
                'is_transactional'  => $c->settings['is_transactional'] ?? null,
                'google_email'      => $c->oauth_token['google_email'] ?? null,
                'microsoft_email'   => $c->oauth_token['microsoft_email'] ?? null,
                'oauth_connected'   => !empty($c->oauth_token['refresh_token'] ?? null),
                'from_email'        => $c->from_email,
                'from_name'         => $c->from_name,
                'force_from_email'  => $c->force_from_email,
                'force_from_name'   => $c->force_from_name,
                'return_path_match' => $c->return_path_match,
                'max_per_hour'      => $c->max_per_hour,
                'is_default'        => $c->is_default,
                'last_tested_at'    => $c->last_tested_at?->toISOString(),
                'last_test_status'  => $c->last_test_status,
            ]);

        return Inertia::render('Sending/SmtpConfig', [
            'configs' => $configs,
        ]);
    }

    private function rules(string $provider, bool $creating): array
    {
        $base = [
            'provider'          => 'required|string|in:' . implode(',', self::ACTIVE_PROVIDERS),
            'name'              => 'required|string|max:100',
            'from_email'        => 'required|email|max:255',
            'from_name'         => 'nullable|string|max:100',
            'force_from_email'  => 'boolean',
            'force_from_name'   => 'boolean',
            'return_path_match' => 'boolean',
            'max_per_hour'      => 'nullable|integer|min:1|max:100000',
            'is_default'        => 'boolean',
        ];

        if ($provider === 'ses') {
            return $base + [
                // username = Access Key, password = Secret Key (encrypted cast)
                'username'        => 'required|string|max:255',
                'password'        => $creating ? 'required|string|max:500' : 'sometimes|nullable|string|max:500',
                'settings'        => 'required|array',
                'settings.region' => 'required|string|in:' . implode(',', self::SES_REGIONS),
            ];
        }

        if ($provider === 'mailgun') {
            return $base + [
                // password = Private API Key (encrypted cast)
                'password'        => $creating ? 'required|string|max:500' : 'sometimes|nullable|string|max:500',
                'settings'        => 'required|array',
                'settings.domain' => 'required|string|max:255',
                'settings.region' => 'required|string|in:us,eu',
            ];
        }

        if ($provider === 'postmark') {
            return $base + [
                // password = Server API Token (encrypted cast)
                'password'                  => $creating ? 'required|string|max:500' : 'sometimes|nullable|string|max:500',
                'settings'                  => 'nullable|array',
                'settings.message_stream'   => 'nullable|string|max:100',
                'settings.track_opens'      => 'boolean',
                'settings.track_links'      => 'nullable|string|in:None,HtmlAndText,HtmlOnly,TextOnly',
            ];
        }

        if ($provider === 'elastic') {
            return $base + [
                // password = API Key (encrypted cast)
                'password'                   => $creating ? 'required|string|max:500' : 'sometimes|nullable|string|max:500',
                'settings'                   => 'nullable|array',
                'settings.is_transactional'  => 'boolean',
            ];
        }

        if ($provider === 'phpmail') {
            // No credentials at all - uses PHP's native mail() equivalent.
            return $base;
        }

        if ($provider === 'gmail') {
            return $base + [
                // username = OAuth Client ID, password = OAuth Client Secret (encrypted cast)
                'username'            => 'required|string|max:255',
                'password'            => $creating ? 'required|string|max:500' : 'sometimes|nullable|string|max:500',
                'oauth'               => $creating ? 'required|array' : 'sometimes|array',
                'oauth.refresh_token' => $creating ? 'required|string' : 'sometimes|nullable|string',
                'oauth.access_token'  => 'nullable|string',
                'oauth.expires_at'    => 'nullable|string',
                'oauth.google_email'  => 'nullable|email',
            ];
        }

        if ($provider === 'outlook') {
            return $base + [
                // username = OAuth Client ID, password = OAuth Client Secret (encrypted cast)
                'username'               => 'required|string|max:255',
                'password'               => $creating ? 'required|string|max:500' : 'sometimes|nullable|string|max:500',
                'oauth'                  => $creating ? 'required|array' : 'sometimes|array',
                'oauth.refresh_token'    => $creating ? 'required|string' : 'sometimes|nullable|string',
                'oauth.access_token'     => 'nullable|string',
                'oauth.expires_at'       => 'nullable|string',
                'oauth.microsoft_email'  => 'nullable|email',
            ];
        }

        if (in_array($provider, self::API_KEY_PROVIDERS, true)) {
            return $base + [
                // password = API Key (encrypted cast)
                'password' => $creating ? 'required|string|max:500' : 'sometimes|nullable|string|max:500',
            ];
        }

        return $base + [
            'host'         => 'required|string|max:255',
            'port'         => 'required|integer|min:1|max:65535',
            'encryption'   => 'required|in:tls,ssl,none',
            'auto_tls'     => 'boolean',
            'auth_enabled' => 'boolean',
            'username'     => 'nullable|string|max:255|required_if:auth_enabled,true',
            'password'     => ($creating ? 'nullable|string|max:500|required_if:auth_enabled,true' : 'sometimes|nullable|string|max:500'),
        ];
    }

    /** API providers send over HTTPS - persist informative connection values. */
    private function applyProviderDefaults(array $data): array
    {
        $provider = $data['provider'] ?? 'smtp';

        if ($provider === 'ses') {
            $region       = $data['settings']['region'] ?? 'us-east-1';
            $data['host'] = "email.{$region}.amazonaws.com";
        }

        if ($provider === 'mailgun') {
            $region       = $data['settings']['region'] ?? 'us';
            $data['host'] = $region === 'eu' ? 'api.eu.mailgun.net' : 'api.mailgun.net';
        }

        if ($provider === 'sendgrid') {
            $data['host'] = 'api.sendgrid.com';
        }

        if ($provider === 'brevo') {
            $data['host'] = 'api.brevo.com';
        }

        if ($provider === 'sparkpost') {
            $data['host'] = 'api.sparkpost.com';
        }

        if ($provider === 'netcore') {
            $data['host'] = 'emailapi.netcorecloud.net';
        }

        if ($provider === 'postmark') {
            $data['host'] = 'api.postmarkapp.com';
        }

        if ($provider === 'elastic') {
            $data['host'] = 'api.elasticemail.com';
        }

        if ($provider === 'smtp2go') {
            $data['host'] = 'api.smtp2go.com';
        }

        if ($provider === 'tosend') {
            $data['host'] = 'api.tosend.com';
        }

        if ($provider === 'phpmail') {
            $data['host'] = 'php://mail';
        }

        if ($provider === 'gmail') {
            $data['host'] = 'gmail.googleapis.com';
        }

        if ($provider === 'outlook') {
            $data['host'] = 'graph.microsoft.com';
        }

        if ($provider !== 'smtp') {
            $data['port']         = 443;
            $data['encryption']   = 'tls';
            $data['auto_tls']     = true;
            $data['auth_enabled'] = true;
        }

        return $data;
    }

    public function store(Request $request)
    {
        $provider = (string) $request->input('provider', 'smtp');
        $data     = $this->applyProviderDefaults(
            $request->validate($this->rules($provider, creating: true))
        );

        if (isset($data['oauth'])) {
            $data['oauth_token'] = $data['oauth'];
            unset($data['oauth']);
        }

        if (!empty($data['is_default'])) {
            SmtpConfig::query()->update(['is_default' => false]);
        }

        // First connection automatically becomes the default
        if (SmtpConfig::count() === 0) {
            $data['is_default'] = true;
        }

        SmtpConfig::create($data);

        return back()->with('success', 'Connection saved.');
    }

    public function update(Request $request, SmtpConfig $smtpConfig)
    {
        $provider = (string) $request->input('provider', $smtpConfig->provider);
        $data     = $this->applyProviderDefaults(
            $request->validate($this->rules($provider, creating: false))
        );

        // Keep existing password / secret key if not provided
        if (empty($data['password'])) {
            unset($data['password']);
        }

        if (isset($data['oauth'])) {
            $data['oauth_token'] = $data['oauth'];
        }
        unset($data['oauth']);

        // Keep the existing tokens unless the user re-authenticated with Google
        if (empty($data['oauth_token']['refresh_token'] ?? null)) {
            unset($data['oauth_token']);
        }

        if (!empty($data['is_default'])) {
            SmtpConfig::where('id', '!=', $smtpConfig->id)->update(['is_default' => false]);
        }

        $smtpConfig->update($data);

        return back()->with('success', 'Connection updated.');
    }

    public function destroy(SmtpConfig $smtpConfig)
    {
        $smtpConfig->delete();
        return back()->with('success', 'Connection deleted.');
    }

    public function setDefault(SmtpConfig $smtpConfig)
    {
        SmtpConfig::query()->update(['is_default' => false]);
        $smtpConfig->update(['is_default' => true]);

        return back()->with('success', "{$smtpConfig->name} set as default.");
    }

    /**
     * Verify a connection by sending a test email.
     *
     * Two modes:
     *  - Form test: connection fields from the add/edit modal.
     *  - Saved test: only `id` (+ optional `to`) - used by the per-card
     *    "Check connection" and "Send test email" actions.
     */
    public function testConnection(Request $request)
    {
        $data = $request->validate([
            'id'              => 'nullable|integer|exists:smtp_configs,id',
            'to'              => 'nullable|email|max:255',
            'provider'        => 'nullable|string|in:' . implode(',', self::ACTIVE_PROVIDERS),
            'host'            => 'nullable|string|max:255',
            'port'            => 'nullable|integer|min:1|max:65535',
            'encryption'      => 'nullable|in:tls,ssl,none',
            'auto_tls'        => 'boolean',
            'auth_enabled'    => 'boolean',
            'username'        => 'nullable|string|max:255',
            'password'        => 'nullable|string|max:500',
            'from_email'      => 'nullable|email|max:255',
            'from_name'       => 'nullable|string|max:100',
            'settings'                => 'nullable|array',
            'settings.region'         => 'nullable|string|max:40',
            'settings.domain'         => 'nullable|string|max:255',
            'settings.message_stream'   => 'nullable|string|max:100',
            'settings.track_opens'      => 'boolean',
            'settings.track_links'      => 'nullable|string|in:None,HtmlAndText,HtmlOnly,TextOnly',
            'settings.is_transactional' => 'boolean',
            'oauth'                     => 'nullable|array',
            'oauth.refresh_token'       => 'nullable|string',
            'oauth.access_token'        => 'nullable|string',
            'oauth.google_email'        => 'nullable|email',
            'oauth.microsoft_email'     => 'nullable|email',
        ]);

        if (isset($data['oauth'])) {
            $data['oauth_token'] = $data['oauth'];
            unset($data['oauth']);
        }

        $saved = !empty($data['id']) ? SmtpConfig::find($data['id']) : null;

        // Fill anything not submitted from the saved profile
        if ($saved) {
            foreach (['provider', 'host', 'port', 'encryption', 'auto_tls', 'auth_enabled', 'username', 'from_email', 'from_name', 'settings'] as $field) {
                $data[$field] = $data[$field] ?? $saved->{$field};
            }

            if (empty($data['password'])) {
                $data['password'] = $saved->password;
            }

            if (empty($data['oauth_token']['refresh_token'] ?? null)) {
                $data['oauth_token'] = $saved->oauth_token;
            }
        }

        $provider = $data['provider'] ?? 'smtp';

        // Provider-specific sanity checks before attempting the send
        if ($provider === 'ses') {
            if (empty($data['username']) || empty($data['password'])) {
                return response()->json(['success' => false, 'message' => 'Access Key and Secret Key are required for testing.'], 422);
            }
        } elseif ($provider === 'mailgun') {
            if (empty($data['password']) || empty($data['settings']['domain'])) {
                return response()->json(['success' => false, 'message' => 'Private API Key and Domain Name are required for testing.'], 422);
            }
        } elseif (in_array($provider, self::API_KEY_PROVIDERS, true)) {
            if (empty($data['password'])) {
                return response()->json(['success' => false, 'message' => 'API Key is required for testing.'], 422);
            }
        } elseif ($provider === 'phpmail') {
            // No credentials needed - nothing to validate.
        } elseif ($provider === 'gmail') {
            if (empty($data['username']) || empty($data['password'])) {
                return response()->json(['success' => false, 'message' => 'Client ID and Client Secret are required for testing.'], 422);
            }
            if (empty($data['oauth_token']['refresh_token'] ?? null)) {
                return response()->json(['success' => false, 'message' => 'Please authenticate with Google to get an access token first.'], 422);
            }
        } elseif ($provider === 'outlook') {
            if (empty($data['username']) || empty($data['password'])) {
                return response()->json(['success' => false, 'message' => 'Client ID and Client Secret are required for testing.'], 422);
            }
            if (empty($data['oauth_token']['refresh_token'] ?? null)) {
                return response()->json(['success' => false, 'message' => 'Please authenticate with Office365 to get an access token first.'], 422);
            }
        } else {
            if (empty($data['host']) || empty($data['port'])) {
                return response()->json(['success' => false, 'message' => 'SMTP host and port are required for testing.'], 422);
            }
            if ((bool) ($data['auth_enabled'] ?? true) && empty($data['password'])) {
                return response()->json(['success' => false, 'message' => 'Password is required for testing.'], 422);
            }
        }

        $toEmail = $data['to']
            ?? auth()->user()?->email
            ?? $data['from_email']
            ?? 'test@example.com';

        $describe = match ($provider) {
            'ses'      => 'Amazon SES (' . ($data['settings']['region'] ?? 'us-east-1') . ')',
            'mailgun'  => 'Mailgun (' . ($data['settings']['domain'] ?? '') . ', ' . strtoupper($data['settings']['region'] ?? 'us') . ')',
            'sendgrid'  => 'SendGrid (API)',
            'brevo'     => 'Brevo (API)',
            'sparkpost' => 'SparkPost (API)',
            'netcore'   => 'Netcore (API)',
            'postmark'  => 'Postmark (API)',
            'elastic'   => 'Elastic Email (API)',
            'smtp2go'   => 'SMTP2GO (API)',
            'tosend'    => 'toSend (API)',
            'phpmail'   => 'PHP Mail (native)',
            'gmail'     => 'Gmail / Google Workspace (' . ($data['oauth_token']['google_email'] ?? 'API') . ')',
            'outlook'   => 'Microsoft / Outlook (' . ($data['oauth_token']['microsoft_email'] ?? 'API') . ')',
            default     => "{$data['host']}:{$data['port']} ({$data['encryption']})",
        };

        try {
            $mailer = SmtpConfigService::configureRuntime($data, $saved);

            Mail::mailer($mailer)->raw(
                "This is a test email from CRMfic to verify your email service connection.\n\nConfiguration tested: {$describe}",
                function ($message) use ($toEmail, $data) {
                    $message->to($toEmail)
                        ->subject('CRMfic Connection Test');

                    if (!empty($data['from_email'])) {
                        $message->from($data['from_email'], $data['from_name'] ?? null);
                    }
                }
            );

            $saved?->update([
                'last_tested_at'   => now(),
                'last_test_status' => 'success',
            ]);

            \App\Models\Notification::notify(
                auth()->id(),
                'success',
                'SMTP Connection Successful',
                "Successfully connected to {$describe}. Test email sent to {$toEmail}.",
                '/sending/smtp'
            );

            return response()->json([
                'success' => true,
                'message' => "Connection successful! Test email sent to {$toEmail}.",
            ]);
        } catch (\Exception $e) {
            $saved?->update([
                'last_tested_at'   => now(),
                'last_test_status' => 'failed',
            ]);

            \App\Models\Notification::notify(
                auth()->id(),
                'error',
                'SMTP Connection Failed',
                "Failed to connect to {$describe}: " . $e->getMessage(),
                '/sending/smtp'
            );

            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
            ], 422);
        }
    }
}
