<?php
declare(strict_types=1);

namespace App\Modules\Settings;

use App\Core\Http\Request;
use App\Core\Http\Response;
use App\Core\I18n\Translator;
use App\Core\Security\Csrf;
use App\Core\Support\Flash;
use App\Core\View\Template;
use App\Modules\Auth\AuthService;
use Throwable;

final class EmailMailboxController
{
    public function __construct(
        private readonly Template $template,
        private readonly Translator $translator,
        private readonly AuthService $auth,
        private readonly EmailMailboxRepository $repository
    ) {
    }

    public function index(Request $request): Response
    {
        $t = $this->translator;
        $mailboxes = $this->repository->listAll();

        $editMailbox = null;
        $editId = (int) $request->input('edit', '0');
        if ($editId > 0) {
            $editMailbox = $this->repository->findById($editId);
        }

        $html = $this->template->render('settings/email-mailboxes', [
            'title' => $t->get('settings.email_mailboxes.title'),
            'activeMenu' => 'settings',
            'activeSettings' => 'email-mailboxes',
            'user' => $this->auth->user(),
            'csrfToken' => Csrf::token(),
            'mailboxes' => $mailboxes,
            'editMailbox' => $editMailbox,
            'successMessage' => Flash::get('settings.email_mailboxes.success', ''),
            'errorMessage' => Flash::get('settings.email_mailboxes.error', ''),
        ], 'layouts/app');

        return Response::html($html);
    }

    public function save(Request $request): Response
    {
        if (!Csrf::validate((string) $request->input('_token', ''))) {
            Flash::set('settings.email_mailboxes.error', 'Nieprawidlowy token CSRF');
            return Response::redirect('/settings/email-mailboxes');
        }

        $name = trim((string) $request->input('name', ''));
        $smtpHost = trim((string) $request->input('smtp_host', ''));
        $smtpUsername = trim((string) $request->input('smtp_username', ''));
        $senderEmail = trim((string) $request->input('sender_email', ''));

        if ($name === '' || $smtpHost === '' || $smtpUsername === '' || $senderEmail === '') {
            Flash::set('settings.email_mailboxes.error', 'Nazwa, serwer SMTP, uzytkownik i e-mail nadawcy sa wymagane');
            return Response::redirect('/settings/email-mailboxes');
        }

        if (!filter_var($senderEmail, FILTER_VALIDATE_EMAIL)) {
            Flash::set('settings.email_mailboxes.error', 'Nieprawidlowy adres e-mail nadawcy');
            return Response::redirect('/settings/email-mailboxes');
        }

        $id = $request->input('id', '');
        $password = (string) $request->input('smtp_password', '');
        if (($id === '' || $id === '0') && $password === '') {
            Flash::set('settings.email_mailboxes.error', 'Haslo SMTP jest wymagane dla nowej skrzynki');
            return Response::redirect('/settings/email-mailboxes');
        }

        try {
            $this->repository->save([
                'id' => $id,
                'name' => $name,
                'smtp_host' => $smtpHost,
                'smtp_port' => $request->input('smtp_port', '587'),
                'smtp_encryption' => $request->input('smtp_encryption', 'tls'),
                'smtp_username' => $smtpUsername,
                'smtp_password' => $password,
                'sender_email' => $senderEmail,
                'sender_name' => $request->input('sender_name', ''),
                'header_html' => $request->input('header_html', ''),
                'footer_html' => $request->input('footer_html', ''),
                'is_default' => $request->input('is_default', null),
                'is_active' => $request->input('is_active', null),
            ]);

            Flash::set('settings.email_mailboxes.success', 'Skrzynka pocztowa zostala zapisana');
        } catch (Throwable) {
            Flash::set('settings.email_mailboxes.error', 'Blad zapisu skrzynki pocztowej');
        }

        return Response::redirect('/settings/email-mailboxes');
    }

    public function delete(Request $request): Response
    {
        if (!Csrf::validate((string) $request->input('_token', ''))) {
            Flash::set('settings.email_mailboxes.error', 'Nieprawidlowy token CSRF');
            return Response::redirect('/settings/email-mailboxes');
        }

        $id = (int) $request->input('id', '0');
        if ($id <= 0) {
            Flash::set('settings.email_mailboxes.error', 'Nieprawidlowy identyfikator skrzynki');
            return Response::redirect('/settings/email-mailboxes');
        }

        try {
            $this->repository->delete($id);
            Flash::set('settings.email_mailboxes.success', 'Skrzynka pocztowa zostala usunieta');
        } catch (Throwable) {
            Flash::set('settings.email_mailboxes.error', 'Blad usuwania skrzynki pocztowej');
        }

        return Response::redirect('/settings/email-mailboxes');
    }

    public function toggleStatus(Request $request): Response
    {
        if (!Csrf::validate((string) $request->input('_token', ''))) {
            Flash::set('settings.email_mailboxes.error', 'Nieprawidlowy token CSRF');
            return Response::redirect('/settings/email-mailboxes');
        }

        $id = (int) $request->input('id', '0');
        if ($id <= 0) {
            Flash::set('settings.email_mailboxes.error', 'Nieprawidlowy identyfikator skrzynki');
            return Response::redirect('/settings/email-mailboxes');
        }

        try {
            $this->repository->toggleStatus($id);
            Flash::set('settings.email_mailboxes.success', 'Status skrzynki zostal zmieniony');
        } catch (Throwable) {
            Flash::set('settings.email_mailboxes.error', 'Blad zmiany statusu skrzynki');
        }

        return Response::redirect('/settings/email-mailboxes');
    }

    public function testConnection(Request $request): Response
    {
        if (!Csrf::validate((string) $request->input('_token', ''))) {
            return Response::json(['success' => false, 'message' => 'Nieprawidlowy token CSRF'], 403);
        }

        $host = trim((string) $request->input('smtp_host', ''));
        $port = (int) $request->input('smtp_port', '587');
        $encryption = (string) $request->input('smtp_encryption', 'tls');
        $username = trim((string) $request->input('smtp_username', ''));
        $password = (string) $request->input('smtp_password', '');

        $existingId = (int) $request->input('id', '0');
        if ($existingId > 0 && $password === '') {
            $existing = $this->repository->findById($existingId);
            if ($existing !== null) {
                $password = (string) ($existing['smtp_password_decrypted'] ?? '');
            }
        }

        if ($host === '' || $username === '') {
            return Response::json(['success' => false, 'message' => 'Serwer SMTP i uzytkownik sa wymagane'], 400);
        }

        $prefix = $encryption === 'ssl' ? 'ssl://' : ($encryption === 'tls' ? 'tls://' : '');
        $address = $prefix . $host . ':' . $port;

        $context = stream_context_create([
            'ssl' => [
                'verify_peer' => false,
                'verify_peer_name' => false,
            ],
        ]);

        $errorMessage = '';
        set_error_handler(function (int $errno, string $errstr) use (&$errorMessage): bool {
            $errorMessage = $errstr;
            return true;
        });

        try {
            $socket = @stream_socket_client($address, $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $context);

            if ($socket === false) {
                $detail = $errstr ?: $errorMessage ?: 'Nie udalo sie polaczyc';
                return Response::json([
                    'success' => false,
                    'message' => "Blad polaczenia z {$host}:{$port} — {$detail}",
                ]);
            }

            $greeting = $this->readSmtpResponse($socket);
            if (!str_starts_with($greeting, '220')) {
                fclose($socket);
                return Response::json([
                    'success' => false,
                    'message' => "Serwer nie odpowiedzial poprawnie: {$greeting}",
                ]);
            }

            $this->sendSmtpCommand($socket, "EHLO orderpro\r\n");
            $ehloResponse = $this->readSmtpResponse($socket);

            if ($encryption === 'tls' && !str_starts_with($prefix, 'tls://')) {
                if (str_contains($ehloResponse, 'STARTTLS')) {
                    $this->sendSmtpCommand($socket, "STARTTLS\r\n");
                    $starttlsResponse = $this->readSmtpResponse($socket);
                    if (!str_starts_with($starttlsResponse, '220')) {
                        fclose($socket);
                        return Response::json([
                            'success' => false,
                            'message' => "STARTTLS nie powiodlo sie: {$starttlsResponse}",
                        ]);
                    }
                    stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
                    $this->sendSmtpCommand($socket, "EHLO orderpro\r\n");
                    $this->readSmtpResponse($socket);
                }
            }

            $this->sendSmtpCommand($socket, "AUTH LOGIN\r\n");
            $authResponse = $this->readSmtpResponse($socket);
            if (!str_starts_with($authResponse, '334')) {
                fclose($socket);
                return Response::json([
                    'success' => false,
                    'message' => "Serwer nie obsluguje AUTH LOGIN: {$authResponse}",
                ]);
            }

            $this->sendSmtpCommand($socket, base64_encode($username) . "\r\n");
            $userResponse = $this->readSmtpResponse($socket);
            if (!str_starts_with($userResponse, '334')) {
                fclose($socket);
                return Response::json([
                    'success' => false,
                    'message' => "Blad uwierzytelniania (uzytkownik): {$userResponse}",
                ]);
            }

            $this->sendSmtpCommand($socket, base64_encode($password) . "\r\n");
            $passResponse = $this->readSmtpResponse($socket);
            if (!str_starts_with($passResponse, '235')) {
                fclose($socket);
                return Response::json([
                    'success' => false,
                    'message' => "Blad uwierzytelniania (haslo): {$passResponse}",
                ]);
            }

            $this->sendSmtpCommand($socket, "QUIT\r\n");
            fclose($socket);

            return Response::json([
                'success' => true,
                'message' => "Polaczenie z {$host}:{$port} powiodlo sie. Uwierzytelnianie OK.",
            ]);
        } catch (Throwable $e) {
            return Response::json([
                'success' => false,
                'message' => "Blad: {$e->getMessage()}",
            ]);
        } finally {
            restore_error_handler();
        }
    }

    /**
     * @param resource $socket
     */
    private function sendSmtpCommand($socket, string $command): void
    {
        fwrite($socket, $command);
    }

    /**
     * @param resource $socket
     */
    private function readSmtpResponse($socket): string
    {
        $response = '';
        stream_set_timeout($socket, 5);

        while ($line = fgets($socket, 512)) {
            $response .= $line;
            if (isset($line[3]) && $line[3] === ' ') {
                break;
            }
        }

        return trim($response);
    }
}
