<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Controller;
use App\Core\Database;
use App\Core\Request;
use App\Core\Session;
use App\Core\Tenant;
use App\Models\SaasCheckoutIntent;
use App\Services\AuditService;
use App\Services\CompanyService;
use App\Services\WompiService;

class SubscriptionController extends Controller
{
    public function result(Request $request): void
    {
        $reference = trim((string) $request->query('reference', ''));
        $transactionId = trim((string) $request->query('id', ''));
        $intentModel = new SaasCheckoutIntent();

        $intent = null;
        if ($reference !== '') {
            $intent = $intentModel->findByReference($reference);
        } elseif ($transactionId !== '') {
            $intent = $intentModel->findByTransactionId($transactionId);
        }

        $accessSlug = trim((string) ($intent['empresa_slug'] ?? ''));
        $adminEmail = trim((string) ($intent['admin_email'] ?? ''));
        $loginParams = [];
        if ($accessSlug !== '') {
            $loginParams['empresa'] = $accessSlug;
        }
        if ($adminEmail !== '') {
            $loginParams['email'] = $adminEmail;
        }

        $this->render('suscripciones/result', [
            'title' => 'Resultado de suscripción',
            'reference' => $reference,
            'transaction_id' => $transactionId,
            'intent' => $intent,
            'status_endpoint' => url('suscripcion/estado'),
            'company_access_url' => $accessSlug !== ''
                ? url('empresa/' . $accessSlug)
                : null,
            'company_short_url' => $accessSlug !== ''
                ? url($accessSlug)
                : null,
            'login_url' => $accessSlug !== '' || $adminEmail !== ''
                ? url('login' . ($loginParams !== [] ? '?' . http_build_query($loginParams) : ''))
                : null,
        ], 'layouts/guest');
    }

    public function status(Request $request): never
    {
        $reference = trim((string) $request->query('reference', ''));
        $transactionId = trim((string) $request->query('id', ''));

        $intentModel = new SaasCheckoutIntent();
        $intent = null;
        if ($reference !== '') {
            $intent = $intentModel->findByReference($reference);
        } elseif ($transactionId !== '') {
            $intent = $intentModel->findByTransactionId($transactionId);
        }

        if (!$intent) {
            $this->json([
                'ok' => false,
                'message' => 'No se encontró el intento de suscripción.',
            ], 404);
        }

        $status = strtolower(trim((string) ($intent['estado'] ?? 'pending')));
        $slug = trim((string) ($intent['empresa_slug'] ?? ''));
        $adminEmail = trim((string) ($intent['admin_email'] ?? ''));
        $loginParams = [];
        if ($slug !== '') {
            $loginParams['empresa'] = $slug;
        }
        if ($adminEmail !== '') {
            $loginParams['email'] = $adminEmail;
        }

        $this->json([
            'ok' => true,
            'reference' => (string) ($intent['reference'] ?? ''),
            'status' => $status,
            'wompi_status' => (string) ($intent['wompi_status'] ?? ''),
            'failure_reason' => (string) ($intent['failure_reason'] ?? ''),
            'company_slug' => $slug,
            'company_access_url' => $slug !== '' ? url('empresa/' . $slug) : null,
            'company_short_url' => $slug !== '' ? url($slug) : null,
            'login_url' => $slug !== '' || $adminEmail !== ''
                ? url('login' . ($loginParams !== [] ? '?' . http_build_query($loginParams) : ''))
                : url('login'),
            'is_provisioned' => $status === 'provisioned',
        ]);
    }

    public function webhook(Request $request): void
    {
        $rawBody = file_get_contents('php://input');
        $payload = json_decode((string) $rawBody, true);

        if (!is_array($payload)) {
            $this->json(['ok' => false, 'message' => 'Payload inválido.'], 400);
        }

        $wompi = new WompiService();
        if (!$wompi->verifyEventChecksum($payload)) {
            $this->json(['ok' => false, 'message' => 'Firma de evento inválida.'], 401);
        }

        $event = (string) ($payload['event'] ?? '');
        if ($event !== 'transaction.updated') {
            $this->json(['ok' => true, 'message' => 'Evento ignorado.']);
        }

        $transaction = is_array($payload['data']['transaction'] ?? null) ? $payload['data']['transaction'] : [];
        $reference = trim((string) ($transaction['reference'] ?? ''));
        $transactionId = trim((string) ($transaction['id'] ?? ''));
        $status = strtoupper(trim((string) ($transaction['status'] ?? '')));
        $amountInCents = (int) ($transaction['amount_in_cents'] ?? 0);
        $currency = strtoupper(trim((string) ($transaction['currency'] ?? $wompi->currency())));

        if ($reference === '') {
            $this->json(['ok' => true, 'message' => 'Sin referencia comercial.']);
        }

        try {
            Database::transaction(function () use (
                $reference,
                $transactionId,
                $status,
                $amountInCents,
                $currency,
                $wompi
            ): void {
                $pdo = Database::connection();
                $intentStmt = $pdo->prepare(
                    'SELECT *
                     FROM saas_checkout_intents
                     WHERE reference = :reference
                     LIMIT 1
                     FOR UPDATE'
                );
                $intentStmt->execute([':reference' => $reference]);
                $intent = $intentStmt->fetch();

                if (!$intent) {
                    return;
                }

                $localStatus = $this->mapWompiStatus($status);

                $updateStatusStmt = $pdo->prepare(
                    'UPDATE saas_checkout_intents
                     SET estado = :estado,
                         wompi_transaction_id = :wompi_transaction_id,
                         wompi_status = :wompi_status,
                         last_event_at = NOW(),
                         updated_at = NOW()
                     WHERE id = :id'
                );
                $updateStatusStmt->execute([
                    ':estado' => $localStatus,
                    ':wompi_transaction_id' => $transactionId !== '' ? $transactionId : null,
                    ':wompi_status' => $status !== '' ? $status : null,
                    ':id' => (int) $intent['id'],
                ]);

                if ($status !== 'APPROVED') {
                    return;
                }

                if ((int) ($intent['empresa_id'] ?? 0) > 0 && (int) ($intent['suscripcion_id'] ?? 0) > 0) {
                    $provisionedStmt = $pdo->prepare(
                        'UPDATE saas_checkout_intents
                         SET estado = "provisioned",
                             paid_at = COALESCE(paid_at, NOW()),
                             provisioned_at = COALESCE(provisioned_at, NOW()),
                             updated_at = NOW()
                         WHERE id = :id'
                    );
                    $provisionedStmt->execute([':id' => (int) $intent['id']]);
                    return;
                }

                if ($amountInCents <= 0 || $amountInCents !== (int) ($intent['amount_in_cents'] ?? 0)) {
                    $failStmt = $pdo->prepare(
                        'UPDATE saas_checkout_intents
                         SET estado = "failed",
                             failure_reason = :failure_reason,
                             updated_at = NOW()
                         WHERE id = :id'
                    );
                    $failStmt->execute([
                        ':failure_reason' => 'Monto del evento no coincide con el intento de checkout.',
                        ':id' => (int) $intent['id'],
                    ]);
                    return;
                }

                if ($currency !== strtoupper((string) ($intent['currency'] ?? 'COP'))) {
                    $failStmt = $pdo->prepare(
                        'UPDATE saas_checkout_intents
                         SET estado = "failed",
                             failure_reason = :failure_reason,
                             updated_at = NOW()
                         WHERE id = :id'
                    );
                    $failStmt->execute([
                        ':failure_reason' => 'Moneda del evento no coincide con el intento de checkout.',
                        ':id' => (int) $intent['id'],
                    ]);
                    return;
                }

                try {
                    $today = date('Y-m-d');
                    $endDate = date('Y-m-d', strtotime('+' . $wompi->paidCycleDays() . ' days'));
                    $companyResult = (new CompanyService())->createCompanyWithAdmin([
                        'nombre' => (string) ($intent['empresa_nombre'] ?? ''),
                        'slug' => (string) ($intent['empresa_slug'] ?? ''),
                        'strict_slug' => true,
                        'email' => (string) ($intent['empresa_email'] ?? ''),
                        'estado' => 'activa',
                    ], [
                        'nombre' => (string) ($intent['admin_nombre'] ?? ''),
                        'email' => (string) ($intent['admin_email'] ?? ''),
                        'password_hash' => (string) ($intent['admin_password_hash'] ?? ''),
                    ], [
                        'plan_id' => (int) ($intent['plan_id'] ?? 0),
                        'fecha_inicio' => $today,
                        'fecha_vencimiento' => $endDate,
                        'estado' => 'activa',
                        'trial' => 0,
                        'auto_renovacion' => 1,
                    ]);

                    $companyId = (int) ($companyResult['company_id'] ?? 0);
                    $subscriptionId = (int) ($companyResult['subscription_id'] ?? 0);
                    if ($companyId <= 0 || $subscriptionId <= 0) {
                        throw new \RuntimeException('No se pudieron generar IDs de aprovisionamiento.');
                    }

                    $paymentStmt = $pdo->prepare(
                        'INSERT INTO pagos_suscripcion (
                            empresa_id, suscripcion_id, pasarela, referencia, monto, moneda, estado, fecha_pago, created_at
                        ) VALUES (
                            :empresa_id, :suscripcion_id, :pasarela, :referencia, :monto, :moneda, :estado, NOW(), NOW()
                        )'
                    );
                    $paymentStmt->execute([
                        ':empresa_id' => $companyId,
                        ':suscripcion_id' => $subscriptionId,
                        ':pasarela' => 'wompi',
                        ':referencia' => $transactionId !== '' ? $transactionId : $reference,
                        ':monto' => $amountInCents / 100,
                        ':moneda' => $currency,
                        ':estado' => 'pagado',
                    ]);

                    $successStmt = $pdo->prepare(
                        'UPDATE saas_checkout_intents
                         SET empresa_id = :empresa_id,
                             suscripcion_id = :suscripcion_id,
                             paid_at = NOW(),
                             provisioned_at = NOW(),
                             estado = "provisioned",
                             failure_reason = NULL,
                             updated_at = NOW()
                         WHERE id = :id'
                    );
                    $successStmt->execute([
                        ':empresa_id' => $companyId,
                        ':suscripcion_id' => $subscriptionId,
                        ':id' => (int) $intent['id'],
                    ]);

                    AuditService::track('saas.checkout.provisioned', 'Empresa auto-creada por webhook Wompi. ID: ' . $companyId);
                } catch (\Throwable $e) {
                    $failStmt = $pdo->prepare(
                        'UPDATE saas_checkout_intents
                         SET estado = "failed",
                             failure_reason = :failure_reason,
                             updated_at = NOW()
                         WHERE id = :id'
                    );
                    $failStmt->execute([
                        ':failure_reason' => substr($e->getMessage(), 0, 250),
                        ':id' => (int) $intent['id'],
                    ]);
                }
            });
        } catch (\Throwable $e) {
            $this->json(['ok' => false, 'message' => 'Error procesando webhook.', 'error' => $e->getMessage()], 500);
        }

        $this->json(['ok' => true, 'message' => 'Evento procesado.']);
    }

    public function accessBySlug(Request $request): never
    {
        $slug = trim((string) $request->param('slug', ''));
        if ($slug === '') {
            flash('error', 'Acceso de empresa inválido.');
            redirect('login');
        }

        $pdo = Database::connection();
        $companyStmt = $pdo->prepare('SELECT id, nombre FROM empresas WHERE slug = :slug LIMIT 1');
        $companyStmt->execute([':slug' => $slug]);
        $company = $companyStmt->fetch();

        if (!$company) {
            flash('error', 'La empresa solicitada no existe.');
            redirect('login');
        }

        $companyId = (int) ($company['id'] ?? 0);

        if (!Auth::check()) {
            Session::put('desired_company_slug', $slug);
            flash('success', 'Ingresa con tu usuario para acceder a ' . (string) ($company['nombre'] ?? 'tu empresa') . '.');
            redirect('login');
        }

        if (!Auth::isSuperadmin()) {
            $accessStmt = $pdo->prepare(
                'SELECT 1
                 FROM usuario_empresa
                 WHERE usuario_id = :usuario_id
                   AND empresa_id = :empresa_id
                 LIMIT 1'
            );
            $accessStmt->execute([
                ':usuario_id' => (int) Auth::id(),
                ':empresa_id' => $companyId,
            ]);

            if (!(bool) $accessStmt->fetchColumn()) {
                flash('error', 'Tu usuario no tiene acceso a esta empresa.');
                redirect('dashboard');
            }
        }

        Tenant::setCompanyId($companyId);
        flash('success', 'Empresa activa: ' . (string) ($company['nombre'] ?? ''));
        redirect('dashboard');
    }

    private function mapWompiStatus(string $status): string
    {
        return match (strtoupper($status)) {
            'APPROVED' => 'approved',
            'DECLINED' => 'declined',
            'VOIDED' => 'voided',
            'ERROR' => 'error',
            default => 'pending',
        };
    }

    private function json(array $payload, int $statusCode = 200): never
    {
        http_response_code($statusCode);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($payload, JSON_UNESCAPED_UNICODE);
        exit;
    }
}
