<?php

declare(strict_types=1);

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Facades\Storage;

/**
 * Company Model.
 *
 * Represents a tenant in the multi-tenant SaaS system.
 * Each company has its own users, credits, clients, and configuration.
 *
 * @property int $id
 * @property string $name
 * @property string|null $business_name
 * @property string|null $tax_id
 * @property string|null $legal_representative
 * @property string|null $logo_path
 * @property string|null $favicon_path
 * @property string $primary_color
 * @property string $secondary_color
 * @property string $interest_method
 * @property string|null $email
 * @property string|null $phone
 * @property string|null $address
 * @property string $timezone
 * @property string $date_format
 * @property string $time_format
 * @property string $locale
 * @property string $status
 * @property Carbon|null $suspended_at
 * @property string|null $suspension_reason
 * @property bool $is_demo
 * @property Carbon $created_at
 * @property Carbon $updated_at
 * @property-read CompanyFinancialSettings|null $financialSettings
 * @property-read Subscription|null $currentSubscription
 * @property-read Collection|User[] $users
 * @property-read Collection|Subscription[] $subscriptions
 * @property-read Collection|SubscriptionRequest[] $subscriptionRequests
 * @property-read Collection|Credit[] $credits
 * @property-read Collection|Client[] $clients
 */
class Company extends Model
{
    use HasFactory;

    // ═══════════════════════════════════════════════════════════════════════════
    // CONSTANTS
    // ═══════════════════════════════════════════════════════════════════════════

    public const STATUS_ACTIVE = 'active';

    public const STATUS_SUSPENDED = 'suspended';

    public const STATUS_INACTIVE = 'inactive';

    public const INTEREST_FLAT_RATE = 'flat_rate';

    public const INTEREST_FLAT_RATE_PROJECTED = 'flat_rate_projected';

    public const INTEREST_DIMINISHING_BALANCE = 'diminishing_balance';

    protected $fillable = [
        'name',
        'business_name',
        'tax_id',
        'legal_representative',
        'logo_path',
        'favicon_path',
        'primary_color',
        'secondary_color',
        'interest_method',
        'email',
        'phone',
        'address',
        'timezone',
        'date_format',
        'time_format',
        'locale',
        'status',
        'suspended_at',
        'suspension_reason',
        'is_demo',
    ];

    protected $casts = [
        'suspended_at' => 'datetime',
        'is_demo' => 'boolean',
    ];

    protected $attributes = [
        'primary_color' => '#3b82f6',
        'secondary_color' => '#64748b',
        'timezone' => 'America/Bogota',
        'date_format' => 'd/m/Y',
        'time_format' => 'H:i',
        'locale' => 'es_CO',
        'status' => self::STATUS_ACTIVE,
        'interest_method' => self::INTEREST_FLAT_RATE,
    ];

    // ═══════════════════════════════════════════════════════════════════════════
    // RELATIONSHIPS
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Company financial settings (1:1).
     */
    public function financialSettings(): HasOne
    {
        return $this->hasOne(CompanyFinancialSettings::class);
    }

    /**
     * All subscriptions for this company.
     */
    public function subscriptions(): HasMany
    {
        return $this->hasMany(Subscription::class);
    }

    /**
     * Subscription requests made by this company.
     */
    public function subscriptionRequests(): HasMany
    {
        return $this->hasMany(SubscriptionRequest::class);
    }

    /**
     * Users belonging to this company.
     */
    public function users(): HasMany
    {
        return $this->hasMany(User::class);
    }

    /**
     * Credits belonging to this company.
     */
    public function credits(): HasMany
    {
        return $this->hasMany(Credit::class);
    }

    /**
     * Clients belonging to this company.
     */
    public function clients(): HasMany
    {
        return $this->hasMany(Client::class);
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // ACCESSORS
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Get the current active subscription.
     */
    public function getCurrentSubscriptionAttribute(): ?Subscription
    {
        return $this->subscriptions()
            ->active()
            ->current()
            ->with('plan')
            ->latest('ends_at')
            ->first();
    }

    /**
     * Get the current plan (via subscription).
     */
    public function getCurrentPlanAttribute(): ?Plan
    {
        return $this->currentSubscription?->plan;
    }

    /**
     * Get display name (business_name or name).
     */
    public function getDisplayNameAttribute(): string
    {
        return $this->business_name ?: $this->name;
    }

    /**
     * Get logo URL.
     */
    public function getLogoUrlAttribute(): ?string
    {
        if (! $this->logo_path) {
            return null;
        }

        return Storage::url($this->logo_path);
    }

    /**
     * Get favicon URL.
     */
    public function getFaviconUrlAttribute(): ?string
    {
        if (! $this->favicon_path) {
            return null;
        }

        return Storage::url($this->favicon_path);
    }

    /**
     * Get status label.
     */
    public function getStatusLabelAttribute(): string
    {
        return match ($this->status) {
            self::STATUS_ACTIVE => 'Activa',
            self::STATUS_SUSPENDED => 'Suspendida',
            self::STATUS_INACTIVE => 'Inactiva',
            default => ucfirst($this->status),
        };
    }

    /**
     * Get status badge color.
     */
    public function getStatusColorAttribute(): string
    {
        return match ($this->status) {
            self::STATUS_ACTIVE => 'success',
            self::STATUS_SUSPENDED => 'danger',
            self::STATUS_INACTIVE => 'gray',
            default => 'gray',
        };
    }

    /**
     * Get interest method label.
     */
    public function getInterestMethodLabelAttribute(): string
    {
        return match ($this->interest_method) {
            self::INTEREST_FLAT_RATE => 'Tasa Fija (Lineal)',
            self::INTEREST_FLAT_RATE_PROJECTED => 'Tasa Fija Proyectada',
            self::INTEREST_DIMINISHING_BALANCE => 'Saldo Deudor',
            default => ucfirst(str_replace('_', ' ', $this->interest_method)),
        };
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // STATUS CHECKS
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Is company active?
     */
    public function isActive(): bool
    {
        return $this->status === self::STATUS_ACTIVE;
    }

    /**
     * Is company suspended?
     */
    public function isSuspended(): bool
    {
        return $this->status === self::STATUS_SUSPENDED;
    }

    /**
     * Is company inactive?
     */
    public function isInactive(): bool
    {
        return $this->status === self::STATUS_INACTIVE;
    }

    /**
     * Check if company has an active subscription.
     */
    public function hasActiveSubscription(): bool
    {
        return $this->currentSubscription !== null;
    }

    /**
     * Check if company is on trial.
     */
    public function isOnTrial(): bool
    {
        return $this->currentSubscription?->isOnTrial() ?? false;
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // SETTINGS HELPERS
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Get or create financial settings.
     */
    public function getFinancialSettings(): CompanyFinancialSettings
    {
        return $this->financialSettings ?? CompanyFinancialSettings::forCompany($this->id);
    }

    /**
     * Get currency code from settings.
     */
    public function getCurrencyCode(): string
    {
        return $this->getFinancialSettings()->currency_code ?? 'COP';
    }

    /**
     * Check if simplified input is enabled.
     */
    public function hasSimplifiedInput(?string $context = null): bool
    {
        return $this->getFinancialSettings()->isSimplifiedInputEnabled($context);
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // SUBSCRIPTION LIMIT HELPERS
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Check if company can add more users.
     */
    public function canAddUser(): bool
    {
        $subscription = $this->currentSubscription;
        if (! $subscription) {
            return false;
        }

        $currentCount = $this->users()->count();

        return $subscription->canAddUser($currentCount);
    }

    /**
     * Check if company can create more credits.
     */
    public function canCreateCredit(): bool
    {
        $subscription = $this->currentSubscription;
        if (! $subscription) {
            return false;
        }

        $activeCreditsCount = $this->credits()
            ->whereIn('status', Credit::ACTIVE_STATUSES)
            ->count();

        return $subscription->canAddCredit($activeCreditsCount);
    }

    /**
     * Check if company can add more collectors (users with the 'collector' role).
     */
    public function canAddCollector(): bool
    {
        $subscription = $this->currentSubscription;
        if (! $subscription) {
            return false;
        }

        $max = $subscription->getMaxCollectors();
        if ($max === 0) {
            return true; // ilimitado
        }

        $collectors = $this->users()
            ->whereHas('roles', fn ($query) => $query->where('name', 'collector'))
            ->count();

        return $collectors < $max;
    }

    /**
     * Check if company can add more clients.
     */
    public function canAddClient(): bool
    {
        $subscription = $this->currentSubscription;
        if (! $subscription) {
            return false;
        }

        $max = $subscription->getMaxClients();
        if ($max === 0) {
            return true; // ilimitado
        }

        return $this->clients()->count() < $max;
    }

    /**
     * Check if company can disburse the given amount without exceeding the
     * monthly volume limit (créditos creados en el mes en curso).
     */
    public function canDisburse(float $amount): bool
    {
        $subscription = $this->currentSubscription;
        if (! $subscription) {
            return false;
        }

        return $subscription->canDisburse($this->currentMonthDisbursedVolume(), $amount);
    }

    /**
     * Dinero NUEVO (caja real) desembolsado en el mes en curso — base del límite de volumen.
     *
     * Es la suma de dos partes, para contar **toda** la caja nueva sin doble-contar:
     *  1. **Créditos raíz** (`parent_credit_id` nulo, no cancelados) creados este mes: su
     *     `amount` es dinero nuevo (préstamo nuevo). Se cuenta por `amount` — robusto: no
     *     depende de que exista el registro de operación.
     *  2. **Caja nueva de operaciones sobre hijos** este mes (#187): refinanciar/renovar/
     *     clonar desembolsan dinero **nuevo** en un crédito hijo (`parent_credit_id` no nulo)
     *     que la parte (1) no ve. Se mide con `FinancialOperation.cash_out`, cuyo invariante
     *     `cash_out = credit_amount − compensated_amount` garantiza que es **solo lo nuevo**
     *     (extender/reestructurar → `cash_out = 0` → no inflan; refinanciar → capital
     *     adicional; renovar → diferencia). Excluye hijos cancelados.
     *
     * Antes solo se sumaba (1) → la caja nueva de refinanciar/renovar/clonar se escapaba del
     * tope (#172 excluía a todos los hijos). Esta suma es **aditiva**: solo puede contar de
     * más caja real, nunca de menos.
     */
    public function currentMonthDisbursedVolume(): float
    {
        $rootsVolume = (float) $this->credits()
            ->whereNull('parent_credit_id')
            ->where('status', '!=', Credit::STATUS_CANCELED)
            ->whereMonth('created_at', now()->month)
            ->whereYear('created_at', now()->year)
            ->sum('amount');

        $childrenNewCash = (float) FinancialOperation::withoutGlobalScopes()
            ->where('company_id', $this->id)
            ->whereMonth('created_at', now()->month)
            ->whereYear('created_at', now()->year)
            ->whereHas('targetCredit', function ($q) {
                $q->withoutGlobalScopes()
                    ->whereNotNull('parent_credit_id')
                    ->where('status', '!=', Credit::STATUS_CANCELED);
            })
            ->sum('cash_out');

        return $rootsVolume + $childrenNewCash;
    }

    /**
     * Get subscription usage statistics.
     */
    public function getUsageStats(): array
    {
        $subscription = $this->currentSubscription;

        $usersCount = $this->users()->count();
        $creditsCount = $this->credits()->whereIn('status', Credit::ACTIVE_STATUSES)->count();
        $clientsCount = $this->clients()->count();

        // Volumen del mes: solo desembolsos reales (excluye transformaciones y cancelados, #172).
        $monthlyVolume = $this->currentMonthDisbursedVolume();

        return [
            'users' => [
                'current' => $usersCount,
                'max' => $subscription?->getMaxUsers() ?? 0,
                'percentage' => $subscription?->getUsagePercentage('max_users', $usersCount) ?? 0,
            ],
            'credits' => [
                'current' => $creditsCount,
                'max' => $subscription?->getMaxActiveCredits() ?? 0,
                'percentage' => $subscription?->getUsagePercentage('max_active_credits', $creditsCount) ?? 0,
            ],
            'clients' => [
                'current' => $clientsCount,
                'max' => $subscription?->getMaxClients() ?? 0,
                'percentage' => $subscription?->getUsagePercentage('max_clients', $clientsCount) ?? 0,
            ],
            'volume' => [
                'current' => $monthlyVolume,
                'max' => $subscription?->getMaxMonthlyVolume() ?? 0,
                'percentage' => $subscription?->getUsagePercentage('max_monthly_volume', $monthlyVolume) ?? 0,
            ],
        ];
    }

    /**
     * Resumen compacto de uso vs límite para la tabla de Compañías.
     * Ej: "Usuarios 3/2 · Clientes 144/100 · Créditos 90/50" (∞ = ilimitado).
     */
    public function getPlanUsageLabel(): string
    {
        if (! $this->currentSubscription) {
            return 'Sin suscripción';
        }

        $stats = $this->getUsageStats();
        $part = function (array $x, string $label): string {
            $max = $x['max'] > 0 ? (string) $x['max'] : '∞';

            return "{$label} {$x['current']}/{$max}";
        };

        return $part($stats['users'], 'Usuarios')
            .' · '.$part($stats['clients'], 'Clientes')
            .' · '.$part($stats['credits'], 'Créditos');
    }

    /**
     * ¿La empresa está por encima de algún límite de conteo del plan?
     * (Ignora `volume`, que es acumulado del mes, no un "cupo" fijo.)
     */
    public function isOverPlanLimit(): bool
    {
        if (! $this->currentSubscription) {
            return false;
        }

        foreach (['users', 'clients', 'credits'] as $key) {
            $stat = $this->getUsageStats()[$key];
            if ($stat['max'] > 0 && $stat['current'] > $stat['max']) {
                return true;
            }
        }

        return false;
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // STATUS TRANSITIONS
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Suspend the company.
     */
    public function suspend(?string $reason = null): bool
    {
        $this->status = self::STATUS_SUSPENDED;
        $this->suspended_at = now();
        $this->suspension_reason = $reason;

        return $this->save();
    }

    /**
     * Reactivate the company.
     */
    public function reactivate(): bool
    {
        $this->status = self::STATUS_ACTIVE;
        $this->suspended_at = null;
        $this->suspension_reason = null;

        return $this->save();
    }

    /**
     * Deactivate the company.
     */
    public function deactivate(): bool
    {
        $this->status = self::STATUS_INACTIVE;

        return $this->save();
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // SCOPES
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Only active companies.
     */
    public function scopeActive($query)
    {
        return $query->where('status', self::STATUS_ACTIVE);
    }

    /**
     * Only suspended companies.
     */
    public function scopeSuspended($query)
    {
        return $query->where('status', self::STATUS_SUSPENDED);
    }

    /**
     * Only the demo company/companies (see App\Support\Demo).
     */
    public function scopeDemo($query)
    {
        return $query->where('is_demo', true);
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // STATIC HELPERS
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Get interest method options.
     */
    public static function getInterestMethodOptions(): array
    {
        return [
            self::INTEREST_FLAT_RATE => 'Tasa Fija (Lineal)',
            self::INTEREST_FLAT_RATE_PROJECTED => 'Tasa Fija Proyectada',
            self::INTEREST_DIMINISHING_BALANCE => 'Saldo Deudor',
        ];
    }

    /**
     * Get status options.
     */
    public static function getStatusOptions(): array
    {
        return [
            self::STATUS_ACTIVE => 'Activa',
            self::STATUS_SUSPENDED => 'Suspendida',
            self::STATUS_INACTIVE => 'Inactiva',
        ];
    }
}
