<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Concerns\ResolvesAdminUserId;
use App\Http\Controllers\Controller;
use App\Models\BalanceTransaction;
use App\Models\SaasAdminAuditLog;
use Carbon\CarbonImmutable;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;

/**
 * SaaS-admin billing-overview для AdminBillingView. Aggregates пополнения и
 * списания за текущий календарный месяц по `balance_transactions`.
 *
 * На MVP без auth-middleware (saas-admin SSO ⏸ Б-1).
 */
class AdminBillingController extends Controller
{
    use ResolvesAdminUserId;

    /** GET /api/admin/billing/tariff-plans — список планов для диалога смены тарифа. */
    public function tariffPlans(): JsonResponse
    {
        $plans = DB::table('tariff_plans')
            ->select(['id', 'name', 'price_monthly'])
            ->orderBy('price_monthly')
            ->get()
            ->map(fn ($p) => [
                'id' => (int) $p->id,
                'name' => $p->name,
                'price_monthly' => (string) $p->price_monthly,
            ]);

        return response()->json(['plans' => $plans]);
    }

    /** PATCH /api/admin/billing/tenants/{id}/status — приостановить/разблокировать тенанта. */
    public function updateStatus(Request $request, int $id): JsonResponse
    {
        $validated = $request->validate([
            'status' => ['required', 'in:active,suspended'],
            'reason' => ['required', 'string', 'min:10', 'max:1000'],
        ]);

        $tenant = $this->findActiveTenant($id);
        $adminUserId = $this->resolveAdminUserId($request, 'system-billing@liderra.local', 'System Billing Bot');

        DB::transaction(function () use ($tenant, $validated, $adminUserId, $request): void {
            // tenants / saas_admin_audit_log не под RLS — SET LOCAL не нужен (ср. refund()).
            DB::table('tenants')->where('id', $tenant->id)->update([
                'status' => $validated['status'],
                'updated_at' => now(),
            ]);

            SaasAdminAuditLog::create([
                'admin_user_id' => $adminUserId,
                'action' => $validated['status'] === 'suspended' ? 'tenant.suspend' : 'tenant.activate',
                'target_type' => 'tenant',
                'target_id' => $tenant->id,
                'target_tenant_id' => $tenant->id,
                'payload_before' => ['status' => $tenant->status],
                'payload_after' => ['status' => $validated['status']],
                'reason' => $validated['reason'],
                'ip_address' => $request->ip() ?? '127.0.0.1',
                'user_agent' => $request->userAgent(),
            ]);
        });

        return response()->json(['id' => $tenant->id, 'status' => $validated['status']]);
    }

    /** POST /api/admin/billing/tenants/{id}/refund — возврат средств: списание с баланса + ledger-запись. */
    public function refund(Request $request, int $id): JsonResponse
    {
        $validated = $request->validate([
            'amount_rub' => ['required', 'numeric', 'gt:0'],
            'reason' => ['required', 'string', 'min:10', 'max:1000'],
        ]);

        $this->findActiveTenant($id); // ранний 404; авторитетный баланс перечитывается под локом ниже
        $amount = number_format((float) $validated['amount_rub'], 2, '.', '');
        $adminUserId = $this->resolveAdminUserId($request, 'system-billing@liderra.local', 'System Billing Bot');

        /** @var array{transaction_id:int, balance_rub:string} $result */
        $result = DB::transaction(function () use ($id, $amount, $validated, $adminUserId, $request): array {
            DB::statement('SET LOCAL app.current_tenant_id = '.$id);

            // Баланс — money-колонка: перечитываем под row-lock внутри транзакции,
            // защита от lost-update (конвенция LedgerService — lockForUpdate на tenants).
            $tenant = DB::table('tenants')->where('id', $id)->whereNull('deleted_at')
                ->lockForUpdate()->first();
            if ($tenant === null) {
                abort(404, 'tenant not found');
            }

            $balance = (string) $tenant->balance_rub;
            if (bccomp($amount, $balance, 2) === 1) {
                abort(422, 'refund amount exceeds tenant balance');
            }
            $newBalance = bcsub($balance, $amount, 2);

            DB::table('tenants')->where('id', $id)->update([
                'balance_rub' => $newBalance,
                'updated_at' => now(),
            ]);

            $tx = BalanceTransaction::create([
                'tenant_id' => $id,
                'type' => BalanceTransaction::TYPE_REFUND,
                'amount_rub' => '-'.$amount,
                'amount_leads' => 0,
                'balance_rub_after' => $newBalance,
                'description' => $validated['reason'],
                'admin_user_id' => $adminUserId,
                'created_at' => now(),
            ]);

            SaasAdminAuditLog::create([
                'admin_user_id' => $adminUserId,
                'action' => 'tenant.refund',
                'target_type' => 'tenant',
                'target_id' => $id,
                'target_tenant_id' => $id,
                'payload_before' => ['balance_rub' => $balance],
                'payload_after' => ['balance_rub' => $newBalance, 'amount_rub' => $amount, 'transaction_id' => $tx->id],
                'reason' => $validated['reason'],
                'ip_address' => $request->ip() ?? '127.0.0.1',
                'user_agent' => $request->userAgent(),
            ]);

            return ['transaction_id' => (int) $tx->id, 'balance_rub' => $newBalance];
        });

        return response()->json([
            'id' => $id,
            'balance_rub' => $result['balance_rub'],
            'transaction_id' => $result['transaction_id'],
        ]);
    }

    /** PATCH /api/admin/billing/tenants/{id}/tariff — сменить тарифный план тенанта. */
    public function changeTariff(Request $request, int $id): JsonResponse
    {
        $validated = $request->validate([
            'tariff_id' => ['required', 'integer', 'exists:tariff_plans,id'],
            'reason' => ['required', 'string', 'min:10', 'max:1000'],
        ]);

        $tenant = $this->findActiveTenant($id);
        $tariff = DB::table('tariff_plans')->where('id', $validated['tariff_id'])->first();
        $adminUserId = $this->resolveAdminUserId($request, 'system-billing@liderra.local', 'System Billing Bot');

        DB::transaction(function () use ($tenant, $tariff, $validated, $adminUserId, $request): void {
            // tenants / saas_admin_audit_log не под RLS — SET LOCAL не нужен (ср. refund()).
            DB::table('tenants')->where('id', $tenant->id)->update([
                'current_tariff_id' => $tariff->id,
                'updated_at' => now(),
            ]);

            SaasAdminAuditLog::create([
                'admin_user_id' => $adminUserId,
                'action' => 'tenant.change_tariff',
                'target_type' => 'tenant',
                'target_id' => $tenant->id,
                'target_tenant_id' => $tenant->id,
                'payload_before' => ['current_tariff_id' => $tenant->current_tariff_id],
                'payload_after' => ['current_tariff_id' => (int) $tariff->id],
                'reason' => $validated['reason'],
                'ip_address' => $request->ip() ?? '127.0.0.1',
                'user_agent' => $request->userAgent(),
            ]);
        });

        return response()->json([
            'id' => $tenant->id,
            'tariff_id' => (int) $tariff->id,
            'tariff_name' => $tariff->name,
        ]);
    }

    /**
     * Возвращает не-удалённого тенанта либо abort(404).
     *
     * @return object{id:int,status:string,balance_rub:string,current_tariff_id:int|null}
     */
    private function findActiveTenant(int $id): object
    {
        $tenant = DB::table('tenants')->where('id', $id)->whereNull('deleted_at')->first();
        if ($tenant === null) {
            abort(404, 'tenant not found');
        }

        return $tenant;
    }

    /** GET /api/admin/billing?search= */
    public function index(Request $request): JsonResponse
    {
        $search = trim((string) $request->query('search', ''));
        $monthStart = now()->startOfMonth();

        $query = DB::table('tenants')
            ->leftJoin('tariff_plans', 'tariff_plans.id', '=', 'tenants.current_tariff_id')
            ->whereNull('tenants.deleted_at');

        if ($search !== '') {
            $like = '%'.$search.'%';
            $query->where(function ($q) use ($like) {
                $q->where('tenants.organization_name', 'ilike', $like)
                    ->orWhere('tenants.subdomain', 'ilike', $like);
            });
        }

        $tenants = $query->select([
            'tenants.id',
            'tenants.subdomain',
            'tenants.organization_name',
            'tenants.contact_email',
            'tenants.status',
            'tenants.balance_rub',
            'tenants.is_trial',
            'tenants.chargeback_unrecovered_rub',
            'tariff_plans.id as tariff_id',
            'tariff_plans.name as tariff_name',
            'tariff_plans.price_monthly as tariff_price_monthly',
        ])->get();

        $tenantIds = $tenants->pluck('id')->all();

        // Aggregate balance_transactions за текущий месяц по типам.
        $aggregates = [];
        if ($tenantIds !== []) {
            $rows = DB::table('balance_transactions')
                ->whereIn('tenant_id', $tenantIds)
                ->where('created_at', '>=', $monthStart)
                ->selectRaw('
                    tenant_id,
                    SUM(CASE WHEN type = ? THEN amount_rub ELSE 0 END) as topups,
                    SUM(CASE WHEN type = ? THEN ABS(amount_rub) ELSE 0 END) as charges,
                    MAX(CASE WHEN type = ? THEN created_at ELSE NULL END) as last_payment_at
                ', ['topup', 'lead_charge', 'topup'])
                ->groupBy('tenant_id')
                ->get();

            foreach ($rows as $r) {
                $aggregates[(int) $r->tenant_id] = [
                    'topups' => (string) $r->topups,
                    'charges' => (string) $r->charges,
                    'last_payment_at' => $r->last_payment_at,
                ];
            }
        }

        $rows = $tenants->map(function ($t) use ($aggregates) {
            $agg = $aggregates[(int) $t->id] ?? [
                'topups' => '0.00',
                'charges' => '0.00',
                'last_payment_at' => null,
            ];

            return [
                'id' => (int) $t->id,
                'subdomain' => $t->subdomain,
                'organization_name' => $t->organization_name,
                'contact_email' => $t->contact_email,
                'status' => $t->status,
                'balance_rub' => (string) $t->balance_rub,
                'tariff_id' => $t->tariff_id !== null ? (int) $t->tariff_id : null,
                'tariff_name' => $t->tariff_name,
                'mrr_rub' => $t->tariff_price_monthly !== null && ! $t->is_trial
                    ? (string) $t->tariff_price_monthly
                    : '0.00',
                'monthly_topups_rub' => $agg['topups'],
                'monthly_charges_rub' => $agg['charges'],
                'last_payment_at' => $agg['last_payment_at'] !== null
                    ? CarbonImmutable::parse($agg['last_payment_at'])->toIso8601String()
                    : null,
                'chargeback_unrecovered_rub' => (string) $t->chargeback_unrecovered_rub,
            ];
        });

        return response()->json([
            'tenants' => $rows,
            'summary' => $this->computeSummary($monthStart),
        ]);
    }

    /**
     * Общая выручка за месяц + кол-во просрочек + возвраты за 30 дн.
     *
     * @return array<string, int|string>
     */
    private function computeSummary(Carbon $monthStart): array
    {
        $thirtyDaysAgo = now()->subDays(30);

        // Total MRR — сумма tariff_price_monthly для не-trial тенантов с активным тарифом.
        $totalMrr = DB::table('tenants')
            ->leftJoin('tariff_plans', 'tariff_plans.id', '=', 'tenants.current_tariff_id')
            ->whereNull('tenants.deleted_at')
            ->where('tenants.is_trial', false)
            ->whereNotNull('tariff_plans.price_monthly')
            ->sum('tariff_plans.price_monthly');

        // Monthly revenue = сумма всех topups за текущий месяц.
        $monthlyRevenue = DB::table('balance_transactions')
            ->where('type', 'topup')
            ->where('created_at', '>=', $monthStart)
            ->sum('amount_rub');

        // Overdue tenants (balance<0 OR chargeback>0).
        $overdueCount = DB::table('tenants')
            ->whereNull('deleted_at')
            ->where(function ($q) {
                $q->where('balance_rub', '<', 0)
                    ->orWhere('chargeback_unrecovered_rub', '>', 0);
            })
            ->count();

        // Refunds за 30 дн — count balance_transactions type='refund'.
        $refundsCount = DB::table('balance_transactions')
            ->where('type', 'refund')
            ->where('created_at', '>=', $thirtyDaysAgo)
            ->count();

        return [
            'total_mrr_rub' => (string) $totalMrr,
            'monthly_revenue_rub' => (string) $monthlyRevenue,
            'overdue_count' => $overdueCount,
            'refunds_count_30d' => $refundsCount,
        ];
    }
}
