<?php

namespace Modules\ClinicCore\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Modules\PortalApi\Entities\PortalOwner;
use Modules\PortalApi\Entities\PortalPushSubscription;
use Modules\PortalApi\Services\Push\PortalPushSender;

/**
 * Clinic-admin engagement panel for the pet-owner PWA:
 *   • Counters: signed-in / installed / push-enabled
 *   • Per-owner table: install state, push categories, device count
 *   • Compose-and-send broadcast: title/body/category/url with an
 *     optional owner_ids[] filter for targeted sends.
 */
class ClcPortalEngagementController extends Controller
{
    public function index(Request $request)
    {
        $businessId = $this->businessId($request);
        if (! $businessId) abort(403);

        $owners = PortalOwner::where('business_id', $businessId)
            ->where('is_active', true)
            ->orderBy('name')
            ->get(['id','name','phone','email','last_login_at']);

        $ownerIds = $owners->pluck('id');

        // Per-owner aggregate from portal_owner_devices
        $deviceAgg = DB::table('portal_owner_devices')
            ->whereIn('owner_id', $ownerIds)
            ->select(
                'owner_id',
                DB::raw('COUNT(*) as device_count'),
                DB::raw('MAX(installed_at) as installed_at'),
                DB::raw('MAX(last_seen_at) as last_seen_at'),
                DB::raw("MAX(platform) as platform")
            )
            ->groupBy('owner_id')
            ->get()
            ->keyBy('owner_id');

        // Per-owner push subs
        $pushAgg = DB::table('portal_push_subscriptions')
            ->whereIn('owner_id', $ownerIds)
            ->select(
                'owner_id',
                DB::raw('COUNT(*) as sub_count'),
                DB::raw("GROUP_CONCAT(DISTINCT JSON_UNQUOTE(JSON_EXTRACT(categories, '$[0]'))) as cat_sample")
            )
            ->groupBy('owner_id')
            ->get()
            ->keyBy('owner_id');

        $rows = $owners->map(function ($o) use ($deviceAgg, $pushAgg) {
            $d = $deviceAgg[$o->id]   ?? null;
            $p = $pushAgg[$o->id]     ?? null;
            return [
                'id'              => $o->id,
                'name'            => $o->name,
                'phone'           => $o->phone,
                'email'           => $o->email,
                'last_login_at'   => $o->last_login_at,
                'installed_at'    => $d->installed_at ?? null,
                'last_seen_at'    => $d->last_seen_at ?? null,
                'platform'        => $d->platform     ?? null,
                'device_count'    => (int) ($d->device_count ?? 0),
                'push_enabled'    => (int) ($p->sub_count ?? 0) > 0,
                'push_devices'    => (int) ($p->sub_count ?? 0),
            ];
        });

        $kpis = [
            'total'         => $owners->count(),
            'seen'          => $rows->where('last_seen_at', '!=', null)->count(),
            'installed'     => $rows->where('installed_at', '!=', null)->count(),
            'push_enabled'  => $rows->where('push_enabled', true)->count(),
        ];

        return view('clinicCore::settings.portal_engagement', compact('rows', 'kpis'));
    }

    public function broadcast(Request $request, PortalPushSender $sender)
    {
        $businessId = $this->businessId($request);
        if (! $businessId) abort(403);

        $data = $request->validate([
            'title'      => 'required|string|max:120',
            'body'       => 'required|string|max:300',
            'url'        => 'nullable|string|max:300',
            'category'   => 'required|in:vaccinations,appointments,lab,marketing',
            'owner_ids'  => 'nullable|array',
            'owner_ids.*'=> 'integer',
        ]);

        $payload = [
            'title' => $data['title'],
            'body'  => $data['body'],
            'url'   => $data['url'] ?: '/app/home',
            'icon'  => asset('app/icon-192.png'),
            'tag'   => 'broadcast-' . substr(uniqid(), -6),
        ];

        if (! empty($data['owner_ids'])) {
            // Restrict targets to owners actually in this business — defence
            // against tampering with the form's hidden owner_ids array.
            $ids = PortalOwner::where('business_id', $businessId)
                ->whereIn('id', $data['owner_ids'])
                ->pluck('id')->all();
            $sent = $sender->sendToOwners($ids, $data['category'], $payload);
            $scopeLabel = count($ids) . ' selected owner(s)';
        } else {
            $sent = $sender->broadcast($data['category'], $payload, $businessId);
            $scopeLabel = 'all owners';
        }

        // Differentiate three states so the operator knows what to do next:
        //   sent > 0           → green "success"
        //   sent = 0 + push lib missing → red "fix backend"
        //   sent = 0 + lib OK  → yellow "nobody is opted-in yet" (not an error)
        if ($sent > 0) {
            return back()->with('status',
                "✓ Push sent to {$sent} device(s) — {$scopeLabel}, category '{$data['category']}'.");
        }
        if (! $sender->isReady()) {
            return back()->with('error',
                'Push library not installed. Run: composer require minishlink/web-push, then try again.');
        }

        // Count push-enabled owners so the message is concrete.
        $pushReady = DB::table('portal_push_subscriptions')
            ->join('portal_owners', 'portal_owners.id', '=', 'portal_push_subscriptions.owner_id')
            ->where('portal_owners.business_id', $businessId)
            ->distinct('portal_owners.id')->count('portal_owners.id');

        return back()->with('warning',
            $pushReady === 0
                ? 'No owners have enabled push notifications yet. Ask them to open the PWA, sign in, go to "Me" and tap Enable.'
                : "Nobody in {$scopeLabel} matched category '{$data['category']}' (they may have opted out). "
                  . "Try another category or send to specific owners.");
    }

    /**
     * Per-owner detail payload — devices, push subscriptions, active
     * sessions, recent activity. JSON so the engagement view can render
     * it inside a modal without a full-page navigation.
     */
    public function ownerDetail(Request $request, int $ownerId)
    {
        $businessId = $this->businessId($request);
        if (! $businessId) abort(403);

        // Defence: only allow inspecting owners in THIS business.
        $owner = PortalOwner::where('id', $ownerId)
            ->where('business_id', $businessId)->first();
        if (! $owner) abort(404);

        $devices = DB::table('portal_owner_devices')
            ->where('owner_id', $ownerId)
            ->orderByDesc('last_seen_at')
            ->get(['id','platform','user_agent','first_seen_at','last_seen_at','installed_at']);

        $subscriptions = DB::table('portal_push_subscriptions')
            ->where('owner_id', $ownerId)
            ->orderByDesc('last_used_at')
            ->get(['id','endpoint','user_agent','categories','last_used_at','last_failed_at','consecutive_failures','created_at']);

        $sessions = DB::table('portal_owner_tokens')
            ->where('owner_id', $ownerId)
            ->where(function ($q) {
                $q->whereNull('expires_at')->orWhere('expires_at', '>', now());
            })
            ->orderByDesc('last_used_at')
            ->get(['id','user_agent','ip','last_used_at','expires_at','created_at']);

        // Lightweight activity log — what we can reconstruct without a
        // dedicated audit table: device ping history + push send results.
        $activity = collect();
        foreach ($devices as $d) {
            if ($d->installed_at) $activity->push([
                'when'  => $d->installed_at,
                'event' => 'Installed to home screen',
                'meta'  => $this->shortUa($d->user_agent),
            ]);
            if ($d->first_seen_at) $activity->push([
                'when'  => $d->first_seen_at,
                'event' => 'First opened the app',
                'meta'  => $this->shortUa($d->user_agent),
            ]);
        }
        foreach ($subscriptions as $s) {
            $activity->push([
                'when'  => $s->created_at,
                'event' => 'Enabled push notifications',
                'meta'  => $this->shortUa($s->user_agent),
            ]);
            if ($s->last_failed_at && $s->consecutive_failures >= 3) {
                $activity->push([
                    'when'  => $s->last_failed_at,
                    'event' => 'Push delivery failing (' . $s->consecutive_failures . ' in a row)',
                    'meta'  => $this->shortUa($s->user_agent),
                ]);
            }
        }
        foreach ($sessions as $sn) {
            $activity->push([
                'when'  => $sn->created_at,
                'event' => 'Signed in',
                'meta'  => ($sn->ip ?? '—') . ' · ' . $this->shortUa($sn->user_agent),
            ]);
        }

        // Pull admin-side audit events for this owner from the global
        // ActivityLog module (sign-out-everywhere, etc.) so the timeline
        // reflects what the clinic did to this account too.
        if (\Schema::hasTable('enhanced_activity_logs')) {
            $audit = DB::table('enhanced_activity_logs')
                ->where('business_id', $businessId)
                ->where('model_type', 'portal_owner')
                ->where('model_id', $ownerId)
                ->orderByDesc('created_at')
                ->take(20)
                ->get(['action','before_data','user_id','ip_address','created_at']);
            foreach ($audit as $a) {
                $payload   = $a->before_data ? json_decode($a->before_data, true) : null;
                $adminName = DB::table('users')->where('id', $a->user_id)->value('username') ?? ('admin #' . $a->user_id);
                $eventCopy = match ($a->action) {
                    'portal_owner_signed_out_everywhere'
                        => 'Admin signed out everywhere · ' . (int)($payload['sessions_revoked'] ?? 0) . ' session(s) revoked',
                    default => 'Admin action: ' . str_replace('_', ' ', $a->action),
                };
                $activity->push([
                    'when'  => $a->created_at,
                    'event' => $eventCopy,
                    'meta'  => 'by ' . $adminName . ' · ' . ($a->ip_address ?? '—'),
                ]);
            }
        }

        $activity = $activity
            ->filter(fn ($a) => $a['when'])
            ->sortByDesc('when')
            ->values()
            ->take(40)
            ->all();

        return response()->json([
            'owner' => [
                'id'    => $owner->id,
                'name'  => $owner->name,
                'phone' => $owner->phone,
                'email' => $owner->email,
                'last_login_at' => $owner->last_login_at,
            ],
            'devices'       => $devices->map(fn ($d) => [
                'id'            => $d->id,
                'platform'      => $d->platform,
                'user_agent'    => $this->shortUa($d->user_agent),
                'first_seen_at' => $d->first_seen_at,
                'last_seen_at'  => $d->last_seen_at,
                'installed_at'  => $d->installed_at,
            ])->all(),
            'subscriptions' => $subscriptions->map(fn ($s) => [
                'id'                   => $s->id,
                'user_agent'           => $this->shortUa($s->user_agent),
                'categories'           => $this->decodeCategories($s->categories),
                'last_used_at'         => $s->last_used_at,
                'last_failed_at'       => $s->last_failed_at,
                'consecutive_failures' => (int) $s->consecutive_failures,
                'endpoint_host'        => $this->endpointHost($s->endpoint),
                'created_at'           => $s->created_at,
            ])->all(),
            'sessions'      => $sessions->map(fn ($sn) => [
                'id'           => $sn->id,
                'user_agent'   => $this->shortUa($sn->user_agent),
                'ip'           => $sn->ip,
                'last_used_at' => $sn->last_used_at,
                'expires_at'   => $sn->expires_at,
                'created_at'   => $sn->created_at,
            ])->all(),
            'activity'      => $activity,
        ]);
    }

    /**
     * Revoke every active session token for this owner, forcing them to
     * re-authenticate on every device. Push subscriptions are NOT touched
     * — they survive logout the same as on most consumer apps.
     *
     * Writes an entry to enhanced_activity_logs (the ActivityLog module's
     * audit trail) so the action is traceable across the whole ERP, not
     * just within this engagement panel. The pre-delete session snapshot
     * is captured into before_data so an auditor can later see exactly
     * which devices were terminated and when.
     */
    public function signOutEverywhere(Request $request, int $ownerId)
    {
        $businessId = $this->businessId($request);
        if (! $businessId) abort(403);

        $owner = PortalOwner::where('id', $ownerId)
            ->where('business_id', $businessId)->first();
        if (! $owner) abort(404);

        // Capture the sessions BEFORE we delete them so the audit row has
        // the full UA / IP / last-used picture for forensics.
        $sessions = DB::table('portal_owner_tokens')
            ->where('owner_id', $ownerId)
            ->get(['id','user_agent','ip','last_used_at','expires_at','created_at'])
            ->map(fn ($s) => [
                'id'           => $s->id,
                'user_agent'   => $this->shortUa($s->user_agent),
                'ip'           => $s->ip,
                'last_used_at' => $s->last_used_at,
                'expires_at'   => $s->expires_at,
                'created_at'   => $s->created_at,
            ])
            ->all();

        $revoked = DB::table('portal_owner_tokens')->where('owner_id', $ownerId)->delete();

        // Audit row — written even if the ActivityLog module isn't fully
        // installed, by checking for the table first. Falls back to the
        // Laravel log otherwise so we never silently lose the event.
        $loggedTo = null;
        try {
            if (\Schema::hasTable('enhanced_activity_logs')) {
                DB::table('enhanced_activity_logs')->insert([
                    'user_id'        => auth()->id(),
                    'business_id'    => $businessId,
                    'action'         => 'portal_owner_signed_out_everywhere',
                    'model_type'     => 'portal_owner',
                    'model_id'       => $owner->id,
                    'model_label'    => $owner->name,
                    'before_data'    => json_encode([
                        'sessions_revoked' => $revoked,
                        'sessions'         => $sessions,
                    ]),
                    'after_data'     => null,
                    'changed_fields' => json_encode([]),
                    'request_url'    => $request->fullUrl(),
                    'request_method' => $request->method(),
                    'ip_address'     => $request->ip(),
                    'user_agent'     => \Illuminate\Support\Str::limit($request->userAgent() ?? '', 490),
                    'created_at'     => now(),
                ]);
                $loggedTo = 'activity_log';
            }
        } catch (\Throwable $e) {
            \Log::warning('[ClcPortalEngagement] activity log write failed', [
                'owner_id' => $owner->id, 'error' => $e->getMessage(),
            ]);
        }

        if (! $loggedTo) {
            \Log::info('[ClcPortalEngagement] sign-out-everywhere', [
                'admin_id'      => auth()->id(),
                'owner_id'      => $owner->id,
                'owner_name'    => $owner->name,
                'sessions_revoked' => $revoked,
                'sessions'      => $sessions,
            ]);
        }

        return response()->json([
            'revoked' => $revoked,
            'message' => "Revoked {$revoked} active session(s). The owner will be signed out on every device. "
                       . "Action recorded in the activity log for audit.",
        ]);
    }

    private function shortUa(?string $ua): string
    {
        if (! $ua) return '—';
        // Pull the OS + browser shorthand that matters most for ops.
        $os = 'Unknown OS';
        if (preg_match('/iPhone OS ([\d_]+)/', $ua, $m))      $os = 'iOS ' . str_replace('_', '.', $m[1]);
        elseif (preg_match('/iPad; CPU OS ([\d_]+)/', $ua, $m)) $os = 'iPadOS ' . str_replace('_', '.', $m[1]);
        elseif (preg_match('/Android ([\d.]+)/', $ua, $m))    $os = 'Android ' . $m[1];
        elseif (preg_match('/Windows NT ([\d.]+)/', $ua, $m)) $os = 'Windows ' . ($m[1] === '10.0' ? '10/11' : $m[1]);
        elseif (preg_match('/Mac OS X ([\d_]+)/', $ua, $m))   $os = 'macOS ' . str_replace('_', '.', $m[1]);
        elseif (preg_match('/Linux/', $ua))                   $os = 'Linux';

        $br = 'Browser';
        if (preg_match('/Edg\/([\d.]+)/', $ua, $m))                  $br = 'Edge ' . explode('.', $m[1])[0];
        elseif (preg_match('/SamsungBrowser\/([\d.]+)/', $ua, $m))   $br = 'Samsung ' . explode('.', $m[1])[0];
        elseif (preg_match('/Firefox\/([\d.]+)/', $ua, $m))          $br = 'Firefox ' . explode('.', $m[1])[0];
        elseif (preg_match('/Chrome\/([\d.]+)/', $ua, $m))           $br = 'Chrome ' . explode('.', $m[1])[0];
        elseif (preg_match('/Version\/([\d.]+).*Safari/', $ua, $m))  $br = 'Safari ' . explode('.', $m[1])[0];

        return "{$br} · {$os}";
    }

    private function decodeCategories($raw): array
    {
        if (is_array($raw)) return $raw;
        $d = json_decode((string) $raw, true);
        return is_array($d) ? $d : [];
    }

    private function endpointHost(?string $endpoint): string
    {
        if (! $endpoint) return '—';
        $p = parse_url($endpoint, PHP_URL_HOST);
        return $p ?: substr($endpoint, 0, 40);
    }

    private function businessId(Request $request): ?int
    {
        return session('user.business_id') ?? auth()->user()?->business_id ?? null;
    }
}
