<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\AccessControl;
use App\Core\Auth;
use App\Core\Csrf;
use App\Core\Request;
use App\Core\Session;
use App\Core\View;
use App\Repositories\DiarInvestigationRepository;
use App\Services\DiarWorkflowAuthorization;
use App\Services\DiarAccessPolicy;
use Throwable;

final class DiarInvestigationController
{
    public function index(): void
    {
        Auth::requireLogin();
        $this->requireRoute('diar-investigations');

        $filters = [
            'q' => trim((string)Request::get('q', '')),
            'site' => trim((string)Request::get('site', '')),
            'status' => trim((string)Request::get('status', '')),
            'severity' => trim((string)Request::get('severity', '')),
            'entity' => trim((string)Request::get('entity', '')),
            'store' => trim((string)Request::get('store', '')),
            'assignee' => trim((string)Request::get('assignee', '')),
            'stage' => trim((string)Request::get('stage', '')),
            'sla' => trim((string)Request::get('sla', '')),
        ];
        $page = max(1, (int)Request::get('p', '1'));
        $perPage = max(20, min(100, (int)Request::get('per_page', '50')));
        $investigationData = [
            'summary' => [],
            'queue' => ['rows' => [], 'total' => 0, 'page' => 1, 'per_page' => $perPage],
            'investigators' => [],
            'filters' => $filters,
            'allowed_sites' => $this->allowedSites(),
        ];
        $loadError = '';

        try {
            $repo = new DiarInvestigationRepository();
            $investigationData['summary'] = $repo->summary($investigationData['allowed_sites'], $this->accessPolicy()->caseScope());
            $investigationData['queue'] = $repo->queue($filters, $investigationData['allowed_sites'], $page, $perPage, $this->accessPolicy()->caseScope());
            $investigationData['investigators'] = $repo->investigators();
        } catch (Throwable $e) {
            $loadError = current_lang() === 'ar'
                ? 'تعذر تحميل قائمة التحقيق في الوقت الحالي.'
                : 'The investigation queue is temporarily unavailable.';
            $this->log($e, 'Index');
        }

        View::render('diar_investigations/index', [
            'title' => current_lang() === 'ar' ? 'قضايا التحقيق' : 'Investigation Cases',
            'activePage' => 'diar-investigations',
            'hospitalCode' => Auth::hospital(),
            'hospital' => \config('hospitals.' . Auth::hospital(), []),
            'investigationData' => $investigationData,
            'loadError' => $loadError,
            'successMessage' => Session::flash('diar_investigation_success'),
            'errorMessage' => Session::flash('diar_investigation_error'),
            'developerBadge' => $this->accessPolicy()->developerBadge(),
            'isAssignedOnly' => $this->accessPolicy()->caseScope()['mode'] === 'assigned',
        ]);
    }

    public function detail(): void
    {
        Auth::requireLogin();
        $this->requireRoute('diar-investigation');

        $caseCode = trim((string)Request::get('case', ''));
        if ($caseCode === '') {
            $this->redirectToQueue();
        }
        $caseData = [];
        $loadError = '';
        $returnUrl = $this->safeReturnUrl((string)Request::get('return', ''));
        try {
            $repo = new DiarInvestigationRepository();
            $caseData = $repo->detail($caseCode, $this->allowedSites(), $this->accessPolicy()->caseScope());
            $caseData['investigators'] = $repo->investigators();
            $caseData['allowed_transitions'] = $repo->allowedTransitions((string)($caseData['case']['status_code'] ?? ''));
        } catch (Throwable $e) {
            if ($e->getMessage() === 'CASE_NOT_FOUND_OR_NOT_ALLOWED') {
                $this->accessPolicy()->redirectHiddenRoute();
            }
            $loadError = current_lang() === 'ar'
                ? 'تعذر تحميل القضية المطلوبة مؤقتًا.'
                : 'The requested case is temporarily unavailable.';
            $this->log($e, 'Detail');
        }

        View::render('diar_investigations/detail', [
            'title' => current_lang() === 'ar' ? 'مراجعة قضية مخاطر الأدوية' : 'Drug Risk Case Review',
            'activePage' => 'diar-investigation',
            'hospitalCode' => Auth::hospital(),
            'hospital' => \config('hospitals.' . Auth::hospital(), []),
            'caseData' => $caseData,
            'loadError' => $loadError,
            'successMessage' => Session::flash('diar_investigation_success'),
            'errorMessage' => Session::flash('diar_investigation_error'),
            'returnUrl' => $returnUrl,
            'capabilities' => $this->workflowCapabilities(is_array($caseData['case'] ?? null) ? $caseData['case'] : []),
            'developerBadge' => $this->accessPolicy()->developerBadge(),
        ]);
    }

    public function evidence(): void
    {
        Auth::requireLogin();
        $this->requireRoute('diar-case-evidence');

        $caseCode = trim((string)Request::get('case', ''));
        $hospitalCode = strtoupper(trim((string)Request::get('hospital', '')));
        $sourceDb = strtoupper(trim((string)Request::get('source_db', '')));
        $snapshotId = max(0, (int)Request::get('snapshot_id', '0'));
        $monthCaseId = max(0, (int)Request::get('month_case_id', '0'));
        $hasFrozenIdentity = $hospitalCode !== '' && $sourceDb !== '' && $snapshotId > 0 && $monthCaseId > 0;

        if ($hasFrozenIdentity && $this->accessPolicy()->caseScope()['mode'] === 'assigned') {
            $this->accessPolicy()->redirectHiddenRoute();
        }

        if (!$hasFrozenIdentity && $caseCode === '') {
            $this->redirectToQueue();
        }

        $caseData = [];
        $loadError = '';
        $returnUrl = $this->safeReturnUrl((string)Request::get('return', ''));
        try {
            $repo = new DiarInvestigationRepository();
            if ($hasFrozenIdentity) {
                $caseData = $repo->frozenEvidenceByIdentity(
                    $hospitalCode,
                    $sourceDb,
                    $snapshotId,
                    $monthCaseId,
                    $this->allowedSites()
                );
            } else {
                $caseData = $repo->frozenEvidence($caseCode, $this->allowedSites(), $this->accessPolicy()->caseScope());
            }
        } catch (Throwable $e) {
            if (in_array($e->getMessage(), ['CASE_NOT_FOUND_OR_NOT_ALLOWED','FROZEN_EVIDENCE_SITE_NOT_ALLOWED'], true)) {
                $this->accessPolicy()->redirectHiddenRoute();
            }
            $loadError = current_lang() === 'ar'
                ? 'تعذر تحميل الأدلة المحفوظة لهذه الحالة مؤقتًا.'
                : 'Preserved evidence is temporarily unavailable.';
            $this->log($e, 'PreservedEvidence');
        }

        View::render('diar_case_evidence/show', [
            'title' => current_lang() === 'ar' ? 'تفاصيل الحالة والأدلة المحفوظة' : 'Case Details & Preserved Evidence',
            'activePage' => 'diar-case-evidence',
            'hospitalCode' => Auth::hospital(),
            'hospital' => \config('hospitals.' . Auth::hospital(), []),
            'caseData' => $caseData,
            'loadError' => $loadError,
            'returnUrl' => $returnUrl,
            'canOpenInvestigation' => $this->accessPolicy()->canOpenRoute('diar-investigation'),
        ]);
    }

    public function update(): void
    {
        Auth::requireLogin();
        $this->requireRoute('diar-investigation');
        $caseCode = trim((string)Request::post('case_code', ''));
        $returnUrl = $this->safeReturnUrl((string)Request::post('return_to', ''));
        if ($caseCode === '') {
            $this->redirectToQueue();
        }
        if (!Csrf::validate((string)Request::post('_csrf', ''))) {
            $this->flashError(current_lang() === 'ar'
                ? 'انتهت صلاحية الجلسة. برجاء إعادة المحاولة.'
                : 'Session expired. Please try again.');
            $this->redirectToCase($caseCode, $returnUrl);
        }

        $action = strtolower(trim((string)Request::post('action', '')));
        $actor = Auth::name();
        $repo = new DiarInvestigationRepository();
        $guard = new DiarWorkflowAuthorization();
        try {
            $case = $repo->find($caseCode, $this->allowedSites(), $this->accessPolicy()->caseScope());
            if (!is_array($case)) {
                throw new \RuntimeException('CASE_NOT_FOUND_OR_NOT_ALLOWED');
            }

            if ($action === 'assign') {
                $this->requireWorkflowCapability($guard->canAssign());
                $repo->assign($caseCode, trim((string)Request::post('assigned_to', '')), trim((string)Request::post('assignment_reason', '')), $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The case was assigned successfully.', 'تم تعيين القضية بنجاح.'));
            } elseif ($action === 'start_review') {
                $this->requireWorkflowCapability($guard->canReview($case));
                $repo->startReview($caseCode, $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('Investigation review started.', 'تم بدء مراجعة التحقيق.'));
            } elseif ($action === 'request_information') {
                $this->requireWorkflowCapability($guard->canRequestInformation($case));
                $repo->requestInformation($caseCode, trim((string)Request::post('requested_from', '')), trim((string)Request::post('request_text', '')), trim((string)Request::post('due_date', '')), $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The information request was recorded.', 'تم تسجيل طلب المعلومات.'));
            } elseif ($action === 'respond_information') {
                $this->requireWorkflowCapability($guard->canRequestInformation($case));
                $repo->respondInformation($caseCode, (int)Request::post('request_id', '0'), trim((string)Request::post('response_text', '')), $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The information response was recorded.', 'تم تسجيل الرد على طلب المعلومات.'));
            } elseif ($action === 'propose_finding') {
                $this->requireWorkflowCapability($guard->canProposeFinding($case));
                $repo->proposeFinding($caseCode, trim((string)Request::post('investigation_outcome', '')), trim((string)Request::post('finding_summary', '')), trim((string)Request::post('root_cause', '')), trim((string)Request::post('recommended_action', '')), (string)Request::post('requires_corrective_action', '0') === '1', $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The finding was submitted for supervisor review.', 'تم تقديم النتيجة لمراجعة المشرف.'));
            } elseif ($action === 'start_supervisor_review') {
                $this->requireWorkflowCapability($guard->canSupervisorReview());
                $repo->startSupervisorReview($caseCode, $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('Supervisor review started.', 'تم بدء مراجعة المشرف.'));
            } elseif ($action === 'supervisor_decision') {
                $this->requireWorkflowCapability($guard->canSupervisorReview());
                $repo->supervisorDecision($caseCode, trim((string)Request::post('decision', '')), trim((string)Request::post('decision_comment', '')), $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The supervisor decision was recorded.', 'تم تسجيل قرار المشرف.'));
            } elseif ($action === 'management_decision') {
                $this->requireWorkflowCapability($guard->canManagementApprove());
                $repo->managementDecision($caseCode, trim((string)Request::post('decision', '')), trim((string)Request::post('decision_comment', '')), $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The management decision was recorded.', 'تم تسجيل قرار الإدارة.'));
            } elseif ($action === 'corrective_create') {
                $this->requireWorkflowCapability($guard->canManageCorrectiveActions());
                $repo->createCorrectiveAction($caseCode, trim((string)Request::post('action_title', '')), trim((string)Request::post('action_description', '')), trim((string)Request::post('action_owner', '')), trim((string)Request::post('action_due_date', '')), $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The corrective action was created.', 'تم إنشاء الإجراء التصحيحي.'));
            } elseif ($action === 'corrective_update') {
                $this->requireWorkflowCapability($guard->canManageCorrectiveActions());
                $repo->updateCorrectiveAction($caseCode, (int)Request::post('corrective_action_id', '0'), trim((string)Request::post('corrective_status', '')), trim((string)Request::post('completion_note', '')), $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The corrective action was updated.', 'تم تحديث الإجراء التصحيحي.'));
            } elseif ($action === 'close_case') {
                $this->requireWorkflowCapability($guard->canClose());
                $repo->closeCase($caseCode, trim((string)Request::post('closure_summary', '')), $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The case was closed.', 'تم إغلاق القضية.'));
            } elseif ($action === 'reopen_case') {
                $this->requireWorkflowCapability($guard->canReopen());
                $repo->reopenCase($caseCode, trim((string)Request::post('reopen_reason', '')), $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The case was reopened.', 'تمت إعادة فتح القضية.'));
            } elseif ($action === 'note') {
                $this->requireWorkflowCapability($guard->canReview($case));
                $repo->addNote($caseCode, trim((string)Request::post('note_type', 'GENERAL')), trim((string)Request::post('note_text', '')), (string)Request::post('is_private', '0') === '1', $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The note was added.', 'تمت إضافة الملاحظة.'));
            } elseif ($action === 'evidence') {
                $this->requireWorkflowCapability($guard->canRecordEvidence($case));
                $repo->addEvidence($caseCode, [
                    'evidence_type' => Request::post('evidence_type', ''),
                    'reference_no' => Request::post('reference_no', ''),
                    'description' => Request::post('description', ''),
                    'file_name' => Request::post('file_name', ''),
                    'storage_path' => Request::post('storage_path', ''),
                    'file_sha256' => Request::post('file_sha256', ''),
                ], $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The evidence reference was recorded.', 'تم حفظ مرجع الدليل.'));
            } elseif ($action === 'transition') {
                // Compatibility path for forms installed before DIAR-6B.4.
                $this->requireWorkflowCapability($guard->canSupervisorReview());
                $repo->transition($caseCode, strtoupper(trim((string)Request::post('new_status', ''))), trim((string)Request::post('investigation_outcome', '')), trim((string)Request::post('resolution_summary', '')), trim((string)Request::post('escalation_level', '')), $actor, $this->allowedSites());
                $this->flashSuccess($this->tr('The legacy case status was updated.', 'تم تحديث الحالة القديمة للقضية.'));
            } else {
                throw new \RuntimeException('UNKNOWN_CASE_ACTION');
            }
        } catch (Throwable $e) {
            if ($e->getMessage() === 'CASE_NOT_FOUND_OR_NOT_ALLOWED') {
                $this->accessPolicy()->redirectHiddenRoute();
            }
            $this->log($e, 'Update:' . $action);
            $this->flashError($this->safeError($e));
        }
        $this->redirectToCase($caseCode, $returnUrl);
    }

    /** @param array<string,mixed> $case @return array<string,bool> */
    private function workflowCapabilities(array $case): array
    {
        $guard = new DiarWorkflowAuthorization();
        $policy = $this->accessPolicy();
        return [
            'assign' => $guard->canAssign(),
            'review' => $guard->canReview($case),
            'evidence' => $guard->canRecordEvidence($case),
            'request_information' => $guard->canRequestInformation($case),
            'propose_finding' => $guard->canProposeFinding($case),
            'supervisor_review' => $guard->canSupervisorReview(),
            'management_approve' => $guard->canManagementApprove(),
            'corrective_action' => $guard->canManageCorrectiveActions(),
            'close' => $guard->canClose(),
            'reopen' => $guard->canReopen(),
            'developer_full_view' => $policy->isDeveloper(),
            'read_only' => $policy->isReadOnlySession(),
            'view_approvals' => $policy->isDeveloper() || $policy->hasAnyRole([
                'DIAR_AUDIT_DIRECTOR','DIAR_INV_SUPERVISOR','DIAR_READ_ONLY_AUDITOR'
            ]),
            'view_corrective_actions' => $policy->isDeveloper() || $policy->hasAnyRole([
                'DIAR_AUDIT_DIRECTOR','DIAR_INV_SUPERVISOR','DIAR_READ_ONLY_AUDITOR'
            ]),
            'view_assignment_history' => $policy->isDeveloper() || $policy->hasAnyRole([
                'DIAR_AUDIT_DIRECTOR','DIAR_INV_SUPERVISOR','DIAR_READ_ONLY_AUDITOR'
            ]),
        ];
    }

    private function requireWorkflowCapability(bool $allowed): void
    {
        if (!$allowed) {
            $this->accessPolicy()->redirectHiddenRoute();
        }
    }

    private function tr(string $en, string $ar): string
    {
        return current_lang() === 'ar' ? $ar : $en;
    }

    /** @return array<int,string> */
    private function allowedSites(): array
    {
        $sites = [];
        if (AccessControl::allowsBranch('njch')) {
            $sites[] = 'NJCH';
        }
        if (AccessControl::allowsBranch('mnh')) {
            $sites[] = 'MNH';
        }
        return $sites;
    }

    private function accessPolicy(): DiarAccessPolicy
    {
        return new DiarAccessPolicy();
    }

    private function requireRoute(string $route): void
    {
        $policy = $this->accessPolicy();
        if (!$policy->canOpenRoute($route)) {
            $policy->redirectHiddenRoute();
        }
    }

    private function safeError(Throwable $e): string
    {
        $code = $e->getMessage();
        $ar = current_lang() === 'ar';
        $messages = [
            'ASSIGNEE_NOT_FOUND' => $ar ? 'المستخدم المحدد غير موجود.' : 'The selected user was not found.',
            'ASSIGNEE_SITE_ACCESS_REQUIRED' => $ar ? 'لا يملك المستخدم المحدد صلاحية الوصول إلى فرع هذه القضية.' : 'The selected user does not have access to this case branch.',
            'INVESTIGATION_OUTCOME_REQUIRED' => $ar ? 'يجب تحديد نتيجة التحقيق.' : 'An investigation outcome is required.',
            'RESOLUTION_OR_ESCALATION_SUMMARY_REQUIRED' => $ar ? 'يجب كتابة ملخص واضح للقرار.' : 'A clear decision summary is required.',
            'ESCALATION_LEVEL_REQUIRED' => $ar ? 'يجب تحديد مستوى التصعيد.' : 'An escalation level is required.',
            'NOTE_TEXT_REQUIRED' => $ar ? 'نص الملاحظة مطلوب.' : 'Note text is required.',
            'EVIDENCE_DESCRIPTION_REQUIRED' => $ar ? 'وصف الدليل مطلوب.' : 'Evidence description is required.',
            'INVALID_EVIDENCE_SHA256' => $ar ? 'بصمة SHA-256 غير صحيحة.' : 'The SHA-256 value is invalid.',
            'CASE_NOT_FOUND_OR_NOT_ALLOWED' => $ar ? 'القضية غير موجودة أو خارج نطاق صلاحيتك.' : 'The case was not found or is outside your access scope.',
            'WORKFLOW_SCHEMA_NOT_READY' => $ar ? 'لم يتم تثبيت مخطط دورة التحقيق بعد.' : 'The investigation workflow schema is not installed.',
            'INFORMATION_REQUEST_FIELDS_REQUIRED' => $ar ? 'الجهة المطلوبة ونص طلب المعلومات مطلوبان.' : 'The requested party and information request are required.',
            'INFORMATION_RESPONSE_REQUIRED' => $ar ? 'يجب اختيار الطلب وكتابة الرد.' : 'Select the request and enter the response.',
            'OPEN_INFORMATION_REQUEST_NOT_FOUND' => $ar ? 'طلب المعلومات المفتوح غير موجود.' : 'The open information request was not found.',
            'FINDING_FIELDS_REQUIRED' => $ar ? 'النتيجة والملخص والسبب الجذري مطلوبة.' : 'Outcome, summary and root cause are required.',
            'SUPERVISOR_REVIEW_REQUIRES_PROPOSED_FINDING' => $ar ? 'يلزم وجود نتيجة مقترحة قبل مراجعة المشرف.' : 'A proposed finding is required before supervisor review.',
            'SUPERVISOR_DECISION_FIELDS_REQUIRED' => $ar ? 'قرار المشرف وتعليقه مطلوبان.' : 'The supervisor decision and comment are required.',
            'MANAGEMENT_DECISION_FIELDS_REQUIRED' => $ar ? 'قرار الإدارة وتعليقها مطلوبان.' : 'The management decision and comment are required.',
            'CORRECTIVE_ACTION_FIELDS_REQUIRED' => $ar ? 'عنوان الإجراء ومالكه مطلوبان.' : 'Corrective action title and owner are required.',
            'CORRECTIVE_COMPLETION_NOTE_REQUIRED' => $ar ? 'ملاحظة الإكمال مطلوبة.' : 'A completion note is required.',
            'OPEN_CORRECTIVE_ACTIONS_PREVENT_CLOSE' => $ar ? 'لا يمكن الإغلاق قبل إكمال أو إلغاء جميع الإجراءات المفتوحة.' : 'Complete or cancel all open corrective actions before closing.',
            'CLOSURE_SUMMARY_REQUIRED' => $ar ? 'ملخص الإغلاق مطلوب.' : 'A closure summary is required.',
            'REOPEN_REASON_REQUIRED' => $ar ? 'سبب إعادة الفتح مطلوب.' : 'A reopen reason is required.',
            'INVALID_DUE_DATE' => $ar ? 'تاريخ الاستحقاق غير صحيح.' : 'The due date is invalid.',
        ];
        if (isset($messages[$code])) {
            return $messages[$code];
        }
        if (strpos($code, 'STATUS_TRANSITION_NOT_ALLOWED') === 0 || strpos($code, 'WORKFLOW_STAGE_NOT_ALLOWED') === 0) {
            return $ar ? 'انتقال مرحلة التحقيق المطلوب غير مسموح.' : 'The requested investigation-stage transition is not allowed.';
        }
        return $ar ? 'لم يتم حفظ التغيير. برجاء مراجعة البيانات والمحاولة مرة أخرى.' : 'The change was not saved. Please review the data and try again.';
    }

    private function flashSuccess(string $message): void
    {
        Session::flash('diar_investigation_success', $message);
    }

    private function flashError(string $message): void
    {
        Session::flash('diar_investigation_error', $message);
    }

    private function redirectToCase(string $caseCode, string $returnUrl = ''): void
    {
        $params = ['case' => $caseCode];
        if ($returnUrl !== '') {
            $params['return'] = $returnUrl;
        }
        \redirect(\url_to('diar-investigation', $params));
    }

    private function safeReturnUrl(string $candidate): string
    {
        $candidate = trim(html_entity_decode($candidate, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'));
        if ($candidate === '' || strpos($candidate, 'index.php?') !== 0) {
            return '';
        }
        if (strpos($candidate, "\r") !== false || strpos($candidate, "\n") !== false) {
            return '';
        }

        $query = (string)parse_url($candidate, PHP_URL_QUERY);
        parse_str($query, $params);
        $page = (string)($params['page'] ?? '');
        if (!$this->accessPolicy()->canOpenRoute($page)) {
            return '';
        }

        return $candidate;
    }

    private function redirectToQueue(): void
    {
        \redirect(\url_to($this->accessPolicy()->homeRoute()));
    }

    private function log(Throwable $e, string $context): void
    {
        error_log('[DiarInvestigation:' . $context . '] ' . $e->getMessage());
    }
}
