<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\Database;
use App\Core\Logger;
use PDO;

class CompanyService
{
    private WelcomeEmailService $welcomeEmailService;

    public function __construct(?WelcomeEmailService $welcomeEmailService = null)
    {
        $this->welcomeEmailService = $welcomeEmailService ?? new WelcomeEmailService();
    }

    public function registerWithAdmin(array $companyData, array $adminData): int
    {
        $created = $this->createCompanyWithAdmin($companyData, $adminData);
        return (int) ($created['company_id'] ?? 0);
    }

    public function createCompanyWithAdmin(
        array $companyData,
        array $adminData,
        array $subscriptionData = []
    ): array {
        $created = Database::transaction(function () use ($companyData, $adminData, $subscriptionData): array {
            $pdo = Database::connection();

            $companyName = trim((string) ($companyData['nombre'] ?? ''));
            if ($companyName === '') {
                throw new \RuntimeException('El nombre de empresa es obligatorio.');
            }

            $slugInput = trim((string) ($companyData['slug'] ?? ''));
            $strictSlug = (bool) ($companyData['strict_slug'] ?? false);
            $slug = $this->resolveCompanySlug($pdo, $slugInput !== '' ? $slugInput : $companyName, $strictSlug);

            $stmt = $pdo->prepare(
                'INSERT INTO empresas (nombre, slug, nit, email, telefono, direccion, moneda, zona_horaria, estado, created_at)
                 VALUES (:nombre, :slug, :nit, :email, :telefono, :direccion, :moneda, :zona_horaria, :estado, NOW())'
            );
            $stmt->execute([
                ':nombre' => $companyName,
                ':slug' => $slug,
                ':nit' => $companyData['nit'] ?? null,
                ':email' => strtolower(trim((string) ($companyData['email'] ?? ''))),
                ':telefono' => $companyData['telefono'] ?? null,
                ':direccion' => $companyData['direccion'] ?? null,
                ':moneda' => $companyData['moneda'] ?? 'COP',
                ':zona_horaria' => $companyData['zona_horaria'] ?? 'America/Bogota',
                ':estado' => $companyData['estado'] ?? 'activa',
            ]);
            $companyId = (int) $pdo->lastInsertId();

            $roleId = (int) $pdo->query("SELECT id FROM roles WHERE slug = 'admin_empresa' LIMIT 1")->fetchColumn();
            if ($roleId === 0) {
                $pdo->exec(
                    "INSERT INTO roles (nombre, slug, descripcion, activo, created_at)
                     VALUES ('Admin Empresa', 'admin_empresa', 'Administrador del tenant', 1, NOW())"
                );
                $roleId = (int) $pdo->lastInsertId();
            }

            $assignPermissionsStmt = $pdo->prepare(
                'INSERT IGNORE INTO rol_permiso (rol_id, permiso_id, created_at)
                 SELECT :rol_id, p.id, NOW()
                 FROM permisos p
                 WHERE p.modulo <> :superadmin_module
                   AND p.codigo NOT LIKE :superadmin_code'
            );
            $assignPermissionsStmt->execute([
                ':rol_id' => $roleId,
                ':superadmin_module' => 'superadmin',
                ':superadmin_code' => 'superadmin.%'
            ]);

            $adminEmail = strtolower(trim((string) ($adminData['email'] ?? '')));
            if ($adminEmail === '') {
                throw new \RuntimeException('El correo del administrador es obligatorio.');
            }

            $passwordHash = trim((string) ($adminData['password_hash'] ?? ''));
            if ($passwordHash === '') {
                $password = (string) ($adminData['password'] ?? '');
                if ($password === '') {
                    throw new \RuntimeException('La contraseña del administrador es obligatoria.');
                }
                $passwordHash = password_hash($password, PASSWORD_DEFAULT);
            }

            $stmt = $pdo->prepare(
                'INSERT INTO usuarios (empresa_id, rol_id, nombre, email, password, activo, created_at)
                 VALUES (:empresa_id, :rol_id, :nombre, :email, :password, 1, NOW())'
            );
            $stmt->execute([
                ':empresa_id' => $companyId,
                ':rol_id' => $roleId,
                ':nombre' => trim((string) ($adminData['nombre'] ?? 'Administrador')),
                ':email' => $adminEmail,
                ':password' => $passwordHash,
            ]);
            $userId = (int) $pdo->lastInsertId();

            $stmt = $pdo->prepare(
                'INSERT INTO usuario_empresa (usuario_id, empresa_id, es_principal, created_at)
                 VALUES (:usuario_id, :empresa_id, 1, NOW())'
            );
            $stmt->execute([
                ':usuario_id' => $userId,
                ':empresa_id' => $companyId,
            ]);

            $subscriptionId = $this->createSubscription($pdo, $companyId, $subscriptionData);

            return [
                'company_id' => $companyId,
                'company_slug' => $slug,
                'user_id' => $userId,
                'subscription_id' => $subscriptionId,
            ];
        });

        $this->sendWelcomeEmailSilently($created, $companyData, $adminData, $subscriptionData);

        return $created;
    }

    private function sendWelcomeEmailSilently(
        array $created,
        array $companyData,
        array $adminData,
        array $subscriptionData
    ): void {
        try {
            $companyId = (int) ($created['company_id'] ?? 0);
            $subscriptionId = (int) ($created['subscription_id'] ?? 0);
            $companySlug = trim((string) ($created['company_slug'] ?? ($companyData['slug'] ?? '')));
            $companyName = trim((string) ($companyData['nombre'] ?? ''));
            $adminName = trim((string) ($adminData['nombre'] ?? 'Administrador'));
            $adminEmail = strtolower(trim((string) ($adminData['email'] ?? '')));
            $planName = trim((string) ($subscriptionData['plan_name'] ?? ''));

            if ($companyId <= 0 || $adminEmail === '') {
                return;
            }

            $sendResult = $this->welcomeEmailService->sendCompanyAdminWelcome([
                'company_id' => $companyId,
                'subscription_id' => $subscriptionId,
                'company_name' => $companyName,
                'company_slug' => $companySlug,
                'admin_name' => $adminName,
                'admin_email' => $adminEmail,
                'plan_name' => $planName,
            ]);

            if ((bool) ($sendResult['ok'] ?? false)) {
                Logger::info('Correo de bienvenida enviado', [
                    'company_id' => $companyId,
                    'admin_email' => $adminEmail,
                ]);
            } else {
                Logger::info('Correo de bienvenida no enviado', [
                    'company_id' => $companyId,
                    'admin_email' => $adminEmail,
                    'error' => (string) ($sendResult['error'] ?? 'Error desconocido'),
                ]);
            }
        } catch (\Throwable $e) {
            Logger::error('Error enviando correo de bienvenida', [
                'error' => $e->getMessage(),
            ]);
        }
    }

    private function createSubscription(PDO $pdo, int $companyId, array $subscriptionData): ?int
    {
        $planId = $this->resolvePlanId($pdo, $subscriptionData);
        if ($planId <= 0) {
            return null;
        }

        $trialDays = (int) config('saas.trial_days', 14);
        $startDate = trim((string) ($subscriptionData['fecha_inicio'] ?? date('Y-m-d')));
        $endDate = trim((string) ($subscriptionData['fecha_vencimiento'] ?? date('Y-m-d', strtotime('+' . $trialDays . ' days'))));

        if (!$this->isDate($startDate)) {
            $startDate = date('Y-m-d');
        }
        if (!$this->isDate($endDate) || $endDate < $startDate) {
            $endDate = $startDate;
        }

        $status = trim((string) ($subscriptionData['estado'] ?? 'activa'));
        $allowedStatuses = ['activa', 'vencida', 'cancelada', 'morosa'];
        if (!in_array($status, $allowedStatuses, true)) {
            $status = 'activa';
        }

        $trial = array_key_exists('trial', $subscriptionData) ? ((bool) $subscriptionData['trial'] ? 1 : 0) : 1;
        $autoRenew = array_key_exists('auto_renovacion', $subscriptionData) ? ((bool) $subscriptionData['auto_renovacion'] ? 1 : 0) : 1;

        $stmt = $pdo->prepare(
            'INSERT INTO suscripciones (
                empresa_id, plan_id, fecha_inicio, fecha_vencimiento, estado, trial, auto_renovacion, created_at
            ) VALUES (
                :empresa_id, :plan_id, :fecha_inicio, :fecha_vencimiento, :estado, :trial, :auto_renovacion, NOW()
            )'
        );
        $stmt->execute([
            ':empresa_id' => $companyId,
            ':plan_id' => $planId,
            ':fecha_inicio' => $startDate,
            ':fecha_vencimiento' => $endDate,
            ':estado' => $status,
            ':trial' => $trial,
            ':auto_renovacion' => $autoRenew,
        ]);

        return (int) $pdo->lastInsertId();
    }

    private function resolvePlanId(PDO $pdo, array $subscriptionData): int
    {
        $planId = (int) ($subscriptionData['plan_id'] ?? 0);
        if ($planId > 0) {
            return $planId;
        }

        $defaultPlanSlug = (string) config('saas.default_plan', 'basico');
        $planStmt = $pdo->prepare('SELECT id FROM planes WHERE slug = :slug LIMIT 1');
        $planStmt->execute([':slug' => $defaultPlanSlug]);

        return (int) $planStmt->fetchColumn();
    }

    private function resolveCompanySlug(PDO $pdo, string $value, bool $strict): string
    {
        $base = $this->slugify($value);
        if ($base === '') {
            $base = 'empresa';
        }

        $exists = $this->companySlugExists($pdo, $base);
        if (!$exists) {
            return $base;
        }

        if ($strict) {
            throw new \RuntimeException('El slug de empresa ya existe. Usa un nombre distinto.');
        }

        for ($i = 2; $i <= 9999; $i++) {
            $candidate = $base . '-' . $i;
            if (!$this->companySlugExists($pdo, $candidate)) {
                return $candidate;
            }
        }

        throw new \RuntimeException('No fue posible generar un slug único para la empresa.');
    }

    private function companySlugExists(PDO $pdo, string $slug): bool
    {
        $stmt = $pdo->prepare('SELECT id FROM empresas WHERE slug = :slug LIMIT 1');
        $stmt->execute([':slug' => $slug]);
        return (bool) $stmt->fetchColumn();
    }

    private function slugify(string $value): string
    {
        $value = strtolower(trim($value));
        $value = preg_replace('/[^a-z0-9]+/i', '-', $value) ?: '';
        return trim($value, '-');
    }

    private function isDate(string $value): bool
    {
        return (bool) preg_match('/^\d{4}-\d{2}-\d{2}$/', $value);
    }
}
