<?php

namespace Modules\VetAI\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Carbon\Carbon;
use Modules\VetAI\Entities\VaiSetting;
use Modules\VetAI\Entities\VaiQuery;
use Modules\VetAI\Services\AiClient;
use Modules\VetClinic\Entities\VcAppointment;

/**
 * Appointment-integrated AI — the 4 staged clinical checkpoints that read the
 * appointment's SOAP data and run an AI advisory step:
 *   exam_review  → review S+O, suggest missed exam / investigations / proceed
 *   diagnosis    → ranked expected diagnoses from S+O+results
 *   treatment    → treatment plan (after the vet approves the diagnosis)
 *   interaction  → drug-interaction & weight-based dose check on the plan
 */
class VaiAppointmentController extends Controller
{
    /** Stage → SOAP field that must be filled before the stage may run. */
    private const STAGES = ['exam_review', 'diagnosis', 'treatment', 'interaction'];

    private function biz()
    {
        return request()->session()->get('user.business_id');
    }

    private function gate()
    {
        if (!auth()->user()->can('vet_ai.use_assistant')) {
            abort(403, 'Unauthorized action.');
        }
    }

    public function generate(Request $request, $appointmentId)
    {
        $this->gate();
        $request->validate(['stage' => 'required|in:' . implode(',', self::STAGES)]);

        $businessId  = $this->biz();
        $settings    = VaiSetting::forBusiness($businessId);
        if (!$settings->isConfigured()) {
            return response()->json(['success' => false, 'msg' => __('vetAI::lang.not_configured')], 422);
        }

        $appointment = VcAppointment::with('pet')->where('business_id', $businessId)->findOrFail($appointmentId);
        $stage       = $request->stage;

        // Server-side gate — treatment advice requires an approved diagnosis.
        if ($stage === 'treatment' && trim((string) $appointment->soap_assessment) === '') {
            return response()->json([
                'success' => false,
                'msg'     => 'Enter the diagnosis in the Assessment field before requesting a treatment plan.',
            ], 422);
        }

        // Monthly token-budget guard
        if ($settings->monthly_token_budget) {
            $used = (int) VaiQuery::forBusiness($businessId)
                ->whereYear('created_at', now()->year)->whereMonth('created_at', now()->month)
                ->sum(DB::raw('COALESCE(tokens_input,0) + COALESCE(tokens_output,0)'));
            if ($used >= $settings->monthly_token_budget) {
                return response()->json(['success' => false, 'msg' => __('vetAI::lang.budget_exceeded')], 422);
            }
        }

        [$system, $userPrompt] = $this->buildStagePrompt($stage, $appointment, $settings);

        $res = app(AiClient::class)->chat($settings, $system, $userPrompt);

        // Treatment stage: post-match recommended drugs against the defined medicines.
        $displayText   = $res['ok'] ? $res['text'] : '';
        $medicineCheck = [];
        if ($res['ok'] && $stage === 'treatment') {
            [$displayText, $medicineCheck] = $this->checkMedicines($res['text'], $businessId);
        }

        $query = VaiQuery::create([
            'business_id'    => $businessId,
            'user_id'        => auth()->id(),
            'appointment_id' => $appointment->id,
            'pet_id'         => $appointment->pet_id,
            'query_type'     => $this->stageQueryType($stage),
            'stage'          => $stage,
            'prompt'         => $userPrompt,
            'response'       => $res['ok'] ? $res['text'] : null,
            'model'          => $settings->model,
            'tokens_input'   => $res['tokens_in'],
            'tokens_output'  => $res['tokens_out'],
            'status'         => $res['ok'] ? 'success' : 'error',
            'error'          => $res['ok'] ? null : $res['error'],
        ]);

        if (!$res['ok']) {
            return response()->json(['success' => false, 'msg' => $res['error'] ?: 'AI request failed.'], 502);
        }

        return response()->json([
            'success'        => true,
            'query_id'       => $query->id,
            'response_html'  => VaiQuery::renderMarkdown($displayText),
            'medicine_check' => $medicineCheck,
            'tokens'         => $res['tokens_in'] + $res['tokens_out'],
            'generated_at'   => $query->created_at->format('d M Y H:i'),
        ]);
    }

    /** Map a stage to the coarse query_type used by the history filters. */
    private function stageQueryType(string $stage): string
    {
        return [
            'exam_review' => 'general',
            'diagnosis'   => 'differential',
            'treatment'   => 'general',
            'interaction' => 'interaction',
        ][$stage] ?? 'general';
    }

    /** Build [systemPrompt, userPrompt] for an appointment stage. */
    private function buildStagePrompt(string $stage, VcAppointment $a, VaiSetting $settings): array
    {
        $default = 'You are an experienced veterinary clinical decision-support assistant working alongside a '
            . 'licensed veterinarian during a live consultation. Be concise, structured and clinic-ready. '
            . 'IMPORTANT: your output is advisory only — the attending veterinarian must independently verify '
            . 'every finding, differential, drug and dose before acting, and remains fully responsible for the '
            . 'diagnosis and treatment. Never present a definitive diagnosis as certain. Flag emergencies '
            . 'prominently. If essential information is missing, state what is needed.';
        $system = trim((string) $settings->system_prompt) ?: $default;

        $patient = $this->patientBlock($a);
        $s = trim((string) ($a->soap_subjective ?: $a->reason));
        $o = trim((string) $a->soap_objective);
        $vitals = $this->vitalsBlock($a);

        switch ($stage) {
            case 'exam_review':
                $user = "TASK: Examination review.\n{$patient}\n\n"
                      . "PRESENTING COMPLAINT / HISTORY (Subjective):\n" . ($s ?: '(not recorded)') . "\n\n"
                      . "PHYSICAL EXAMINATION (Objective):\n" . ($o ?: '(not recorded)') . "\n"
                      . ($vitals ? "VITALS: {$vitals}\n" : '')
                      . "\nReview the workup so far and respond with ONE clear recommendation:\n"
                      . "(A) additional or more specific PHYSICAL EXAMINATION steps that appear to be missing, or\n"
                      . "(B) recommended INVESTIGATIONS (lab tests / imaging) with a brief rationale, or\n"
                      . "(C) state that the examination is sufficient to proceed to a diagnosis.\n"
                      . "Start your answer with the chosen option letter.";
                break;

            case 'diagnosis':
                $user = "TASK: Expected / differential diagnoses.\n{$patient}\n\n"
                      . "SUBJECTIVE:\n" . ($s ?: '(not recorded)') . "\n\n"
                      . "OBJECTIVE:\n" . ($o ?: '(not recorded)') . "\n"
                      . ($vitals ? "VITALS: {$vitals}\n" : '')
                      . "\nINVESTIGATION RESULTS:\n" . $this->investigationResults($a) . "\n\n"
                      . "Provide a ranked list of the most likely diagnoses. For each: brief supporting reasoning, "
                      . "and what would confirm or exclude it.";
                break;

            case 'treatment':
                $user = "TASK: Treatment plan.\n{$patient}\n\n"
                      . "CONFIRMED DIAGNOSIS (approved by the attending veterinarian):\n"
                      . trim((string) $a->soap_assessment) . "\n\n"
                      . ($s ? "Relevant history: {$s}\n" : '')
                      . ($o ? "Relevant exam: {$o}\n" : '')
                      . "\nProvide a practical treatment plan: medications (drug, mg/kg dose with the calculated "
                      . "dose for this patient's weight, route, frequency, duration), supportive care, monitoring "
                      . "and client advice.\n"
                      . "At the VERY END of your answer add one line in EXACTLY this format, listing only the "
                      . "drug / active-ingredient names you recommended (no doses):\n"
                      . "MEDICINES: name1, name2, name3";
                break;

            case 'interaction':
            default:
                $plan = trim((string) $a->soap_plan);
                $meds = $this->planMedications($a);
                $user = "TASK: Drug interaction & weight-based dosing check.\n{$patient}\n\n"
                      . "PLANNED TREATMENT / MEDICATIONS:\n" . ($meds ?: ($plan ?: '(none recorded)')) . "\n\n"
                      . "Check for clinically significant drug interactions and species/patient contraindications. "
                      . "For each medication give the weight-based dose (mg/kg and the calculated mg for this "
                      . "patient), route and frequency, plus monitoring advice.";
                break;
        }

        return [$system, $user];
    }

    private function patientBlock(VcAppointment $a): string
    {
        $pet  = $a->pet;
        $bits = [];
        if ($pet) {
            if ($pet->species) $bits[] = 'Species: ' . $pet->species;
            if ($pet->breed)   $bits[] = 'Breed: ' . $pet->breed;
            if ($pet->dob)     $bits[] = 'Age: ' . Carbon::parse($pet->dob)->age . ' yr';
            if ($pet->gender)  $bits[] = 'Sex: ' . $pet->gender;
        }
        $weight = $a->weight_kg ?: ($pet->weight_kg ?? null);
        if ($weight) $bits[] = 'Weight: ' . $weight . ' kg';

        if ($pet) {
            try {
                $conds = $pet->conditions()->whereIn('status', ['active', 'chronic'])->with('condition')->get()
                    ->map(fn($c) => optional($c->condition)->name)->filter()->implode(', ');
                if ($conds) $bits[] = 'Known conditions: ' . $conds;
            } catch (\Throwable $e) {
            }
        }
        return 'PATIENT — ' . ($bits ? implode(' | ', $bits) : '(not specified)');
    }

    private function vitalsBlock(VcAppointment $a): string
    {
        $v = [];
        if ($a->temperature)      $v[] = 'Temp ' . $a->temperature;
        if ($a->heart_rate)       $v[] = 'HR ' . $a->heart_rate;
        if ($a->respiratory_rate) $v[] = 'RR ' . $a->respiratory_rate;
        return implode(', ', $v);
    }

    private function investigationResults(VcAppointment $a): string
    {
        $lines = [];
        $exams = is_array($a->exam_results) ? $a->exam_results : [];
        foreach ($exams as $x) {
            if (!is_array($x) || ($x['status'] ?? '') !== 'done') continue;
            $label  = $x['label'] ?? 'Test';
            $kind   = ($x['kind'] ?? '') === 'lab' ? 'Lab' : 'Exam';
            $result = trim((string) ($x['result_text'] ?? ''));
            $lines[] = "- {$kind} — {$label}: " . ($result !== '' ? $result : '(result attached / pending text)');
        }
        // Fall back to the free-text investigation fields
        foreach (['soap_lab_investigations' => 'Lab notes', 'soap_exam_investigations' => 'Exam notes'] as $col => $tag) {
            $txt = trim((string) ($a->{$col} ?? ''));
            if ($txt !== '') $lines[] = "- {$tag}: {$txt}";
        }
        return $lines ? implode("\n", $lines) : '(no investigation results recorded yet)';
    }

    private function planMedications(VcAppointment $a): string
    {
        $json = $a->soap_plan_meds_json ?? null;
        if (is_string($json) && $json !== '') {
            $decoded = json_decode($json, true);
            if (is_array($decoded)) {
                $names = [];
                foreach ($decoded as $m) {
                    if (is_array($m)) {
                        $names[] = trim(($m['name'] ?? $m['label'] ?? '') . ' ' . ($m['dose'] ?? ''));
                    } elseif (is_string($m)) {
                        $names[] = trim($m);
                    }
                }
                $names = array_filter($names);
                if ($names) return implode("\n", array_map(fn($n) => '- ' . $n, $names));
            }
        }
        return '';
    }

    /**
     * Post-match the AI's recommended drugs against the clinic's DEFINED
     * medicines (vc_medicines) — no formulary is sent in the prompt.
     * Returns [displayText (MEDICINES line stripped), checkRows].
     */
    private function checkMedicines(string $text, int $businessId): array
    {
        $display = $text;
        $check   = [];

        if (preg_match('/^[ \t]*MEDICINES:[ \t]*(.+)$/im', $text, $m)) {
            $display = trim(preg_replace('/^[ \t]*MEDICINES:[ \t]*.+$/im', '', $text));
            $drugs   = array_filter(array_map(fn($d) => trim($d, " \t.;-—*"), explode(',', $m[1])));

            foreach (array_unique($drugs) as $drug) {
                if ($drug === '') continue;
                $row = null;
                try {
                    if (Schema::hasTable('vc_medicines')) {
                        $q = DB::table('vc_medicines')->where('business_id', $businessId);
                        $q->where(function ($w) use ($drug) {
                            $w->where('name', 'like', '%' . $drug . '%');
                            if (Schema::hasColumn('vc_medicines', 'generic_name')) {
                                $w->orWhere('generic_name', 'like', '%' . $drug . '%');
                            }
                        });
                        $row = $q->first(['id', 'name']);
                    }
                } catch (\Throwable $e) {
                    $row = null;
                }
                $check[] = [
                    'drug'    => $drug,
                    'in_list' => (bool) $row,
                    'matched' => $row->name ?? null,
                ];
            }
        }

        return [$display, $check];
    }
}
