<?php

namespace App\Services\Api;

use App\Models\ApiClient;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class NgbvdApiStatsService
{
    public function incidentStatistics(array $filters = [], ?ApiClient $client = null): array
    {
        $client = $client ?: request()->attributes->get('api_client');

        $query = DB::table('incident_data')
            ->leftJoin('victim', 'incident_data.victimid', '=', 'victim.id')
            ->leftJoin('incidents', 'incident_data.incidentid', '=', 'incidents.id')
            ->leftJoin('districts', 'incident_data.districtid', '=', 'districts.id')
            ->leftJoin('gender', 'victim.gender', '=', 'gender.id')
            ->leftJoin('national_status', 'victim.nationality_status', '=', 'national_status.id')
            ->leftJoin('religion', 'victim.religion', '=', 'religion.id');

        if ($client) {
            app(ApiClientScopeService::class)->applyToIncidentQuery($query, $client, 'incident_data', 'victim');
        } elseif (request()->is('api/*')) {
            // Zero-trust default for API statistics: no client identity means no data.
            $query->whereRaw('1 = 0');
        }

        if (!empty($filters['startdate'])) {
            $query->whereDate('incident_data.incident_date', '>=', $filters['startdate']);
        }

        if (!empty($filters['enddate'])) {
            $query->whereDate('incident_data.incident_date', '<=', $filters['enddate']);
        }

        if (!empty($filters['district_id'])) {
            $requestedDistrict = (string) $filters['district_id'];

            if ($client && $client->hasDataScope('district') && ! $client->hasDataScope('national') && ! $client->canUseScope('*')) {
                $allowed = array_map('strval', app(ApiClientScopeService::class)->allowedDistrictIds($client));

                if (! in_array($requestedDistrict, $allowed, true)) {
                    $query->whereRaw('1 = 0');
                } else {
                    $query->where('incident_data.districtid', $requestedDistrict);
                }
            } else {
                $query->where('incident_data.districtid', $requestedDistrict);
            }
        }

        if (!empty($filters['incident_id'])) {
            $query->where('incident_data.incidentid', $filters['incident_id']);
        }

        $rows = $query->select([
                'incident_data.id as incident_record_id',
                'incidents.name as incident_type',
                'districts.id as district_id',
                'districts.name as district_name',
                'gender.name as gender_name',
                'national_status.name as national_status_name',
                'religion.name as religion_name',
            ])
            ->get();

        $quarantined = $rows->filter(function ($row) {
            return trim((string) $row->incident_type) === '';
        })->count();

        $validRows = $rows->filter(function ($row) {
            return trim((string) $row->incident_type) !== '';
        });

        $incidents = $validRows->groupBy(function ($row) {
            return $this->normalizeKey($row->incident_type);
        })->map(function ($items) {
            return [
                'total_incidents' => $items->count(),
                'districts' => $this->countBy($items, 'district_name'),
                'gender' => $this->ensureKeys($this->countBy($items, 'gender_name'), ['Male', 'Female', 'Unknown']),
                'national_status' => $this->countBy($items, 'national_status_name'),
                'religion' => $this->countBy($items, 'religion_name'),
            ];
        })->sortKeys()->toArray();

        return [
            'metadata' => [
                'api_version' => 'v2',
                'data_as_of' => now()->toIso8601String(),
                'filters_applied' => $filters,
                'record_count' => $validRows->count(),
            ],
            'incidents' => $incidents,
        ];
    }

    private function countBy($items, string $field): array
    {
        return $items->groupBy(function ($row) use ($field) {
            return $this->normalizeKey($row->{$field} ?? null);
        })->map(function ($group) {
            return $group->count();
        })->sortKeys()->toArray();
    }

    private function normalizeKey($value): string
    {
        $value = trim((string) $value);
        if ($value === '') {
            return 'Unknown';
        }

        return Str::title(preg_replace('/\s+/', ' ', $value));
    }

    private function ensureKeys(array $data, array $keys): array
    {
        foreach ($keys as $key) {
            if (!array_key_exists($key, $data)) {
                $data[$key] = 0;
            }
        }

        ksort($data);

        return $data;
    }

}
