<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

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

/**
 * SaaS-admin incidents-log lookup для AdminIncidentsView.
 *
 * Чтение `incidents_log` (schema §9). На MVP без auth (saas-admin SSO ⏸ Б-1).
 *
 * Возвращает list инцидентов с дренажным фильтром по type/severity и summary
 * статистикой (open/investigating/RKN-pending). `incident_id` — derived from
 * `id` в формате `INC-YYYY-MMDD-NNNN` (frontend ожидает строковый код).
 */
class AdminIncidentsController extends Controller
{
    use ResolvesAdminUserId;

    /**
     * SaaS-level tables (`incidents_log`, `tenants`, `saas_admin_users`) читаются
     * под BYPASSRLS-ролью `crm_supplier_worker`: у дефолтной `crm_app_user` нет
     * грантов на `incidents_log` → `permission denied`. Паттерн соответствует
     * остальной cross-tenant cron-инфраструктуре (incidents:watch-failures,
     * scheduler:check-heartbeats, audit:verify-chains).
     */
    private const DB_CONNECTION = 'pgsql_supplier';

    /** GET /api/admin/incidents?type=&severity=&unresolved_only=&limit=&offset= */
    public function index(Request $request): JsonResponse
    {
        $type = (string) $request->query('type', '');
        $severity = (string) $request->query('severity', '');
        $unresolvedOnly = $request->boolean('unresolved_only');
        $limit = max(1, min(500, (int) $request->query('limit', '100')));
        $offset = max(0, (int) $request->query('offset', '0'));

        $query = DB::connection(self::DB_CONNECTION)->table('incidents_log');

        if ($type !== '') {
            $query->where('type', $type);
        }
        if ($severity !== '') {
            $query->where('severity', $severity);
        }
        if ($unresolvedOnly) {
            $query->whereNull('resolved_at');
        }

        $total = (clone $query)->count();

        $rows = $query
            ->orderByDesc('started_at')
            ->orderByDesc('id')
            ->limit($limit)
            ->offset($offset)
            ->get();

        return response()->json([
            'incidents' => $rows->map(fn ($r) => [
                'id' => (int) $r->id,
                'incident_id' => $this->formatIncidentId($r),
                'type' => $r->type,
                'severity' => $r->severity,
                'summary' => $r->summary,
                'started_at' => CarbonImmutable::parse($r->started_at)->toIso8601String(),
                'detected_at' => CarbonImmutable::parse($r->detected_at)->toIso8601String(),
                'resolved_at' => $r->resolved_at !== null
                    ? CarbonImmutable::parse($r->resolved_at)->toIso8601String()
                    : null,
                'status' => $this->deriveStatus($r),
                'affected_tenants_count' => is_array($r->affected_tenant_ids)
                    ? count($r->affected_tenant_ids)
                    : (is_string($r->affected_tenant_ids) ? $this->parsePgArray($r->affected_tenant_ids) : 0),
                'affected_users_count' => $r->affected_users_count !== null ? (int) $r->affected_users_count : null,
                'rkn_notified' => $r->rkn_notified_at !== null,
                'rkn_notified_at' => $r->rkn_notified_at !== null
                    ? CarbonImmutable::parse($r->rkn_notified_at)->toIso8601String()
                    : null,
                'rkn_deadline_at' => $r->type === 'data_breach' && $r->rkn_notified_at === null
                    ? CarbonImmutable::parse($r->detected_at)->addHours(24)->toIso8601String()
                    : null,
            ]),
            'total' => $total,
            'limit' => $limit,
            'offset' => $offset,
            'summary' => $this->computeSummary(),
        ]);
    }

    /** POST /api/admin/incidents/{id}/rkn-notify — зафиксировать уведомление РКН (G6, 152-ФЗ). */
    public function notifyRkn(Request $request, int $id): JsonResponse
    {
        $row = DB::connection(self::DB_CONNECTION)->table('incidents_log')->where('id', $id)->first();
        if ($row === null) {
            abort(404, 'incident not found');
        }
        if ($row->type !== 'data_breach') {
            abort(422, 'РКН-уведомление применимо только к инцидентам типа data_breach');
        }
        if ($row->rkn_notified_at !== null) {
            abort(409, 'РКН уже уведомлён по этому инциденту');
        }

        $adminUserId = $this->resolveAdminUserId($request, 'system-incidents@liderra.local', 'System Incidents Bot');

        DB::connection(self::DB_CONNECTION)->transaction(function () use ($row, $adminUserId, $request): void {
            DB::connection(self::DB_CONNECTION)->table('incidents_log')->where('id', $row->id)->update([
                'rkn_notified_at' => now(),
                'updated_at' => now(),
            ]);

            SaasAdminAuditLog::create([
                'admin_user_id' => $adminUserId,
                'action' => 'incident.rkn_notify',
                'target_type' => 'incident',
                'target_id' => $row->id,
                'payload_before' => ['rkn_notified_at' => null],
                'payload_after' => ['rkn_notified_at' => now()->toIso8601String()],
                'reason' => 'Роскомнадзор уведомлён об утечке ПДн через админ-интерфейс (152-ФЗ).',
                'ip_address' => $request->ip() ?? '127.0.0.1',
                'user_agent' => $request->userAgent(),
            ]);
        });

        return $this->show($id);
    }

    /** GET /api/admin/incidents/{id} — полная карточка инцидента (drill-down G5). */
    public function show(int $id): JsonResponse
    {
        $row = DB::connection(self::DB_CONNECTION)->table('incidents_log')->where('id', $id)->first();
        if ($row === null) {
            abort(404, 'incident not found');
        }

        $tenantIds = is_array($row->affected_tenant_ids)
            ? $row->affected_tenant_ids
            : ($row->affected_tenant_ids !== null ? $this->parsePgArrayValues((string) $row->affected_tenant_ids) : []);

        $tenants = $tenantIds === []
            ? collect()
            : DB::connection(self::DB_CONNECTION)->table('tenants')->whereIn('id', $tenantIds)
                ->select(['id', 'organization_name'])->get();

        $admins = DB::connection(self::DB_CONNECTION)->table('saas_admin_users')
            ->whereIn('id', array_filter([$row->created_by_admin_id, $row->closed_by_admin_id]))
            ->pluck('full_name', 'id');

        return response()->json([
            'incident' => [
                'id' => (int) $row->id,
                'incident_id' => $this->formatIncidentId($row),
                'type' => $row->type,
                'severity' => $row->severity,
                'summary' => $row->summary,
                'root_cause' => $row->root_cause,
                'postmortem_url' => $row->postmortem_url,
                'started_at' => CarbonImmutable::parse($row->started_at)->toIso8601String(),
                'detected_at' => CarbonImmutable::parse($row->detected_at)->toIso8601String(),
                'resolved_at' => $row->resolved_at !== null
                    ? CarbonImmutable::parse($row->resolved_at)->toIso8601String() : null,
                'status' => $this->deriveStatus($row),
                'affected_tenants' => $tenants->map(fn ($t) => [
                    'id' => (int) $t->id,
                    'organization_name' => $t->organization_name,
                ])->values(),
                'affected_users_count' => $row->affected_users_count !== null ? (int) $row->affected_users_count : null,
                'notification_sent_at' => $row->notification_sent_at !== null
                    ? CarbonImmutable::parse($row->notification_sent_at)->toIso8601String() : null,
                'rkn_notified' => $row->rkn_notified_at !== null,
                'rkn_notified_at' => $row->rkn_notified_at !== null
                    ? CarbonImmutable::parse($row->rkn_notified_at)->toIso8601String() : null,
                'rkn_deadline_at' => $row->type === 'data_breach' && $row->rkn_notified_at === null
                    ? CarbonImmutable::parse($row->detected_at)->addHours(24)->toIso8601String() : null,
                'created_by_admin' => $admins->get($row->created_by_admin_id),
                'closed_by_admin' => $row->closed_by_admin_id !== null ? $admins->get($row->closed_by_admin_id) : null,
                'created_at' => $row->created_at !== null
                    ? CarbonImmutable::parse($row->created_at)->toIso8601String() : null,
                'updated_at' => $row->updated_at !== null
                    ? CarbonImmutable::parse($row->updated_at)->toIso8601String() : null,
            ],
        ]);
    }

    /**
     * PG-array literal '{1,2,3}' → массив int.
     *
     * @return list<int>
     */
    private function parsePgArrayValues(string $literal): array
    {
        $trimmed = trim($literal, '{}');
        if ($trimmed === '') {
            return [];
        }

        return array_map('intval', explode(',', $trimmed));
    }

    /** Уникальный человеко-читаемый ID: INC-YYYY-MMDD-NNNN, NNNN = id padded. */
    private function formatIncidentId(object $row): string
    {
        $started = CarbonImmutable::parse($row->started_at);

        return sprintf('INC-%s-%s-%04d', $started->format('Y'), $started->format('md'), (int) $row->id);
    }

    /** Status derive: resolved_at!=null → resolved; иначе investigating если detected_at есть; иначе open. */
    private function deriveStatus(object $row): string
    {
        if ($row->resolved_at !== null) {
            return 'resolved';
        }
        if ($row->detected_at !== null) {
            return 'investigating';
        }

        return 'open';
    }

    /** PG-array literal '{1,2,3}' → count элементов. */
    private function parsePgArray(string $literal): int
    {
        $trimmed = trim($literal, '{}');
        if ($trimmed === '') {
            return 0;
        }

        return count(explode(',', $trimmed));
    }

    /**
     * Open / investigating / РКН-pending counts для page-head.
     *
     * @return array<string, int>
     */
    private function computeSummary(): array
    {
        $base = DB::connection(self::DB_CONNECTION)->table('incidents_log');

        return [
            'open' => (clone $base)->whereNull('resolved_at')->whereNull('detected_at')->count(),
            'investigating' => (clone $base)->whereNull('resolved_at')->whereNotNull('detected_at')->count(),
            'rkn_pending' => (clone $base)
                ->where('type', 'data_breach')
                ->whereNull('rkn_notified_at')
                ->whereNull('resolved_at')
                ->count(),
            'total_unresolved' => (clone $base)->whereNull('resolved_at')->count(),
        ];
    }
}
