<?php

namespace App\Services\Incidents;

use App\Models\Configuration;
use App\Models\CurrentIncident;
use App\Models\Incident;
use App\Models\Student;
use App\Models\StudentClass;
use Illuminate\Support\Facades\DB;

class CurrentIncidentService
{
    public function __construct(private IncidentLookupService $lookup)
    {
    }

    public function listCurrent(): array
    {
        $incidents = CurrentIncident::query()->get()->map(fn ($row) => $row->toArray())->all();
        $schoolYear = Configuration::getConfig('school_year');

        $updaterIds = [];
        foreach ($incidents as $incident) {
            foreach (['updated_by_open', 'updated_by_closed', 'updated_by_canceled'] as $key) {
                if (!empty($incident[$key])) {
                    $updaterIds[] = (int) $incident[$key];
                }
            }
        }

        $updaterMap = $this->lookup->updaterNameMap($updaterIds);
        foreach ($incidents as &$incident) {
            foreach (['updated_by_open', 'updated_by_closed', 'updated_by_canceled'] as $key) {
                $id = (int) ($incident[$key] ?? 0);
                $incident[$key . '_name'] = $id > 0 ? ($updaterMap[$id] ?? null) : null;
            }
        }
        unset($incident);

        return [
            'incidents' => $incidents,
            'grades' => $this->lookup->gradeOptionsWithActiveStudents($schoolYear),
        ];
    }

    public function studentsByGrade(int $gradeId): array
    {
        $studentIds = StudentClass::query()
            ->active()
            ->where('student_class.class_section_id', $gradeId)
            ->pluck('student_id')
            ->all();

        if (empty($studentIds)) {
            return [];
        }

        return Student::query()
            ->whereIn('id', $studentIds)
            ->where('is_active', 1)
            ->get()
            ->map(fn ($student) => [
                'id' => (int) $student->id,
                'name' => trim((string) $student->firstname . ' ' . (string) $student->lastname),
            ])
            ->all();
    }

    public function addIncident(array $payload, int $userId): array
    {
        $studentId = (int) $payload['student_id'];
        $incidentType = (string) $payload['incident'];
        $description = (string) ($payload['description'] ?? '');
        $incidentState = (string) ($payload['incident_state'] ?? 'Open');
        $stateDescription = (string) ($payload['state_description'] ?? '');
        $grade = (string) $payload['grade'];

        $student = Student::query()->find($studentId);
        if (!$student) {
            return [
                'ok' => false,
                'message' => 'Student not found.',
            ];
        }

        $semester = Configuration::getConfig('semester');
        $schoolYear = Configuration::getConfig('school_year');
        $currentDateTime = function_exists('utc_now') ? utc_now() : now('UTC')->toDateTimeString();

        $existingIncident = CurrentIncident::query()
            ->where('student_id', $studentId)
            ->where('incident', $incidentType)
            ->first();

        if ($existingIncident) {
            $data = [
                'incident_datetime' => $currentDateTime,
                'updated_by_open' => $userId,
            ];

            if ($incidentState === 'Closed') {
                $data['incident_state'] = 'Closed';
                $data['close_description'] = $this->appendLine($existingIncident->close_description ?? '', $stateDescription);
            } elseif ($incidentState === 'Canceled') {
                $data['incident_state'] = 'Canceled';
                $data['cancel_description'] = $this->appendLine($existingIncident->cancel_description ?? '', $stateDescription);
            } else {
                $data['open_description'] = $this->appendLine($existingIncident->open_description ?? '', $description);
            }

            $existingIncident->fill($data);
            $existingIncident->save();

            return [
                'ok' => true,
                'created' => false,
                'incident_id' => (int) $existingIncident->id,
            ];
        }

        $incident = CurrentIncident::query()->create([
            'student_id' => $student->id,
            'student_name' => trim((string) $student->firstname . ' ' . (string) $student->lastname),
            'grade' => $grade,
            'incident' => $incidentType,
            'incident_datetime' => $currentDateTime,
            'incident_state' => 'Open',
            'updated_by_open' => $userId,
            'open_description' => $description,
            'semester' => $semester,
            'school_year' => $schoolYear,
        ]);

        return [
            'ok' => true,
            'created' => true,
            'incident_id' => (int) $incident->id,
        ];
    }

    public function updateState(int $incidentId, string $newState): bool
    {
        return CurrentIncident::query()->whereKey($incidentId)->update([
            'incident_state' => $newState,
        ]) > 0;
    }

    public function closeIncident(int $incidentId, string $closeDescription, ?string $actionTaken, int $userId): array
    {
        $incident = CurrentIncident::query()->find($incidentId);
        if (!$incident) {
            return ['ok' => false, 'message' => 'Incident not found.'];
        }

        if ($incident->incident_state === 'Closed') {
            return ['ok' => false, 'message' => 'Incident is already closed.'];
        }

        $incident->fill([
            'incident_state' => 'Closed',
            'updated_by_closed' => $userId,
            'close_description' => $closeDescription,
            'action_taken' => $actionTaken,
        ]);
        $incident->save();

        return $this->moveToHistory($incident);
    }

    public function cancelIncident(int $incidentId, ?string $cancelDescription, ?string $actionTaken, int $userId): array
    {
        $incident = CurrentIncident::query()->find($incidentId);
        if (!$incident) {
            return ['ok' => false, 'message' => 'Incident not found.'];
        }

        if ($incident->incident_state === 'Canceled') {
            return ['ok' => false, 'message' => 'Incident is already canceled.'];
        }

        $incident->fill([
            'incident_state' => 'Canceled',
            'updated_by_canceled' => $userId,
            'cancel_description' => $cancelDescription,
            'action_taken' => $actionTaken,
        ]);
        $incident->save();

        return $this->moveToHistory($incident);
    }

    private function moveToHistory(CurrentIncident $incident): array
    {
        return DB::transaction(function () use ($incident) {
            $history = Incident::query()->create([
                'student_id' => $incident->student_id,
                'student_name' => $incident->student_name,
                'grade' => $incident->grade,
                'incident' => $incident->incident,
                'incident_datetime' => $incident->incident_datetime,
                'incident_state' => $incident->incident_state,
                'updated_by_open' => $incident->updated_by_open,
                'open_description' => $incident->open_description,
                'updated_by_closed' => $incident->updated_by_closed,
                'close_description' => $incident->close_description,
                'action_taken' => $incident->action_taken,
                'updated_by_canceled' => $incident->updated_by_canceled,
                'cancel_description' => $incident->cancel_description,
                'semester' => $incident->semester,
                'school_year' => $incident->school_year,
                'created_at' => $incident->created_at,
                'updated_at' => $incident->updated_at,
            ]);

            $incident->delete();

            return [
                'ok' => true,
                'incident_id' => (int) $history->id,
            ];
        });
    }

    private function appendLine(string $existing, string $append): string
    {
        if ($existing === '') {
            return $append;
        }

        return $existing . PHP_EOL . $append;
    }
}
