<?php

namespace App\Services\Families;

use App\Models\Family;
use App\Models\Student;
use App\Models\StudentClass;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class FamilyQueryService
{
    public function __construct(private FamilyFinanceService $finance)
    {
    }

    public function listStudentsForSelect(): array
    {
        return DB::table('students')
            ->select('id', DB::raw("CONCAT(lastname, ', ', firstname) AS name"))
            ->orderBy('lastname')
            ->orderBy('firstname')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();
    }

    public function searchSuggestions(string $query, int $limit = 8): array
    {
        $query = trim($query);
        if ($query === '') {
            return [];
        }

        $qs = '%' . str_replace(['%', '_'], ['\\%', '\\_'], $query) . '%';

        $students = DB::table('students')
            ->select('id', 'firstname', 'lastname')
            ->whereRaw("CONCAT_WS(' ', firstname, lastname) LIKE ?", [$qs])
            ->orderBy('lastname')
            ->orderBy('firstname')
            ->limit($limit)
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();

        $guardians = DB::table('users')
            ->select('id', 'firstname', 'lastname', 'email', 'cellphone')
            ->where(function ($q) use ($qs) {
                $q->whereRaw("CONCAT_WS(' ', firstname, lastname) LIKE ?", [$qs])
                    ->orWhere('email', 'like', $qs)
                    ->orWhere('cellphone', 'like', $qs);
            })
            ->orderBy('lastname')
            ->orderBy('firstname')
            ->limit($limit)
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();

        $items = [];
        foreach ($students as $s) {
            $items[] = [
                'type' => 'student',
                'id' => (int) ($s['id'] ?? 0),
                'label' => trim(($s['firstname'] ?? '') . ' ' . ($s['lastname'] ?? '')),
                'sub' => 'Student',
            ];
        }
        foreach ($guardians as $g) {
            $items[] = [
                'type' => 'guardian',
                'id' => (int) ($g['id'] ?? 0),
                'label' => trim(($g['firstname'] ?? '') . ' ' . ($g['lastname'] ?? '')),
                'sub' => trim(($g['email'] ?? '') . ' ' . ($g['cellphone'] ?? '')),
            ];
        }

        return $items;
    }

    public function resolveStudentIdByGuardian(int $guardianId): ?int
    {
        if ($guardianId <= 0) {
            return null;
        }

        $row = DB::table('family_guardians as fg')
            ->join('family_students as fs', 'fs.family_id', '=', 'fg.family_id')
            ->join('students as s', 's.id', '=', 'fs.student_id')
            ->where('fg.user_id', $guardianId)
            ->orderBy('s.lastname')
            ->orderBy('s.firstname')
            ->select('s.id as student_id')
            ->first();
        if ($row && !empty($row->student_id)) {
            return (int) $row->student_id;
        }

        $fallback = Student::query()
            ->select('id')
            ->where('parent_id', $guardianId)
            ->orderBy('lastname')
            ->orderBy('firstname')
            ->first();

        return $fallback?->id ? (int) $fallback->id : null;
    }

    public function listFamiliesByStudent(int $studentId): array
    {
        return DB::table('family_students as fs')
            ->join('families as f', 'f.id', '=', 'fs.family_id')
            ->where('fs.student_id', $studentId)
            ->where('f.is_active', 1)
            ->orderByDesc('fs.is_primary_home')
            ->orderBy('f.household_name')
            ->select('f.*', 'fs.is_primary_home')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();
    }

    public function listGuardiansByFamily(int $familyId): array
    {
        return DB::table('family_guardians as fg')
            ->join('users as u', 'u.id', '=', 'fg.user_id')
            ->where('fg.family_id', $familyId)
            ->orderByDesc('fg.is_primary')
            ->orderBy('u.lastname')
            ->orderBy('u.firstname')
            ->select(
                'u.id as user_id',
                'u.firstname',
                'u.lastname',
                'u.email',
                'u.cellphone',
                'u.address_street',
                'u.city',
                'u.state',
                'u.zip',
                'fg.relation',
                'fg.is_primary',
                'fg.receive_emails',
                'fg.receive_sms'
            )
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();
    }

    public function adminIndexData(?int $studentId, ?int $guardianId, string $schoolYear): array
    {
        $studentId = (int) ($studentId ?? 0);
        $guardianId = (int) ($guardianId ?? 0);

        $resolvedStudentId = null;
        if ($studentId <= 0 && $guardianId > 0) {
            $resolvedStudentId = $this->resolveStudentIdByGuardian($guardianId);
            if ($resolvedStudentId) {
                $studentId = $resolvedStudentId;
            }
        }

        $data = [
            'student' => null,
            'families' => [],
            'guardians' => [],
            'students' => $this->listStudentsForSelect(),
            'searchStudents' => DB::table('students')
                ->select('id', 'firstname', 'lastname')
                ->orderBy('lastname')
                ->orderBy('firstname')
                ->get()
                ->map(fn ($r) => (array) $r)
                ->all(),
            'searchGuardians' => DB::table('family_guardians as fg')
                ->join('users as u', 'u.id', '=', 'fg.user_id')
                ->distinct()
                ->select('u.id', 'u.firstname', 'u.lastname', 'u.email', 'u.cellphone')
                ->orderBy('u.lastname')
                ->orderBy('u.firstname')
                ->get()
                ->map(fn ($r) => (array) $r)
                ->all(),
            'resolved_student_id' => $resolvedStudentId,
        ];

        if ($studentId > 0) {
            $data['student'] = DB::table('students')
                ->select('id', 'firstname', 'lastname')
                ->where('id', $studentId)
                ->first();
            $data['student'] = $data['student'] ? (array) $data['student'] : null;

            $families = DB::table('family_students as fs')
                ->join('families as f', 'f.id', '=', 'fs.family_id')
                ->where('fs.student_id', $studentId)
                ->orderByDesc('fs.is_primary_home')
                ->orderBy('f.household_name')
                ->select(
                    'f.id',
                    'f.family_code',
                    'f.household_name',
                    'f.address_line1',
                    'f.address_line2',
                    'f.city',
                    'f.state',
                    'f.postal_code',
                    'f.country',
                    'f.primary_phone',
                    'f.preferred_lang',
                    'f.preferred_contact_method',
                    'f.is_active',
                    'fs.is_primary_home'
                )
                ->get()
                ->map(fn ($r) => (array) $r)
                ->all();

            foreach ($families as &$fam) {
                $familyId = (int) ($fam['id'] ?? 0);
                $fam['guardians'] = $this->listGuardiansByFamily($familyId);
                $fam['students'] = $this->studentsForFamily($familyId, $schoolYear);

                $parentIds = array_values(array_filter(array_map(
                    static fn ($g) => (int) ($g['user_id'] ?? 0),
                    $fam['guardians']
                )));
                $finance = $this->finance->loadFinancialsForParents($parentIds);
                $fam['invoices'] = $finance['invoices'];
                $fam['payments'] = $finance['payments'];
                $fam['finance_summary'] = $finance['summary'];
                $fam['invoice_map'] = $finance['invoice_map'];
            }
            unset($fam);
            $data['families'] = $families;

            if (!empty($families)) {
                $familyId = (int) ($families[0]['id'] ?? 0);
                $data['guardians'] = $familyId ? $this->listGuardiansByFamily($familyId) : [];
            }
        }

        return $data;
    }

    public function familyCard(?int $studentId, ?int $guardianId, ?int $familyId, string $schoolYear): ?array
    {
        $studentId = (int) ($studentId ?? 0);
        $guardianId = (int) ($guardianId ?? 0);
        $familyId = (int) ($familyId ?? 0);

        if ($familyId <= 0) {
            if ($studentId > 0) {
                $row = DB::table('family_students as fs')
                    ->join('families as f', 'f.id', '=', 'fs.family_id')
                    ->where('fs.student_id', $studentId)
                    ->orderByDesc('fs.is_primary_home')
                    ->orderBy('f.household_name')
                    ->select('f.id')
                    ->first();
                $familyId = $row?->id ? (int) $row->id : 0;
            } elseif ($guardianId > 0) {
                $row = DB::table('family_guardians as fg')
                    ->join('families as f', 'f.id', '=', 'fg.family_id')
                    ->where('fg.user_id', $guardianId)
                    ->orderBy('f.household_name')
                    ->select('f.id')
                    ->first();
                if ($row?->id) {
                    $familyId = (int) $row->id;
                } else {
                    $fallback = DB::table('students as s')
                        ->join('family_students as fs', 'fs.student_id', '=', 's.id')
                        ->join('families as f', 'f.id', '=', 'fs.family_id')
                        ->where('s.parent_id', $guardianId)
                        ->orderByDesc('fs.is_primary_home')
                        ->orderBy('f.household_name')
                        ->select('f.id')
                        ->first();
                    $familyId = $fallback?->id ? (int) $fallback->id : 0;
                }
            }
        }

        if ($familyId <= 0) {
            return null;
        }

        $family = Family::query()
            ->select(
                'id',
                'family_code',
                'household_name',
                'address_line1',
                'address_line2',
                'city',
                'state',
                'postal_code',
                'country',
                'primary_phone',
                'preferred_lang',
                'preferred_contact_method',
                'is_active'
            )
            ->find($familyId);

        if (!$family) {
            return null;
        }

        $payload = $family->toArray();
        $payload['guardians'] = $this->listGuardiansByFamily($familyId);
        $payload['students'] = $this->studentsForFamily($familyId, $schoolYear);
        $payload['emergency_contacts'] = $this->emergencyContactsForParents($payload['guardians']);

        $parentIds = array_values(array_filter(array_map(
            static fn ($g) => (int) ($g['user_id'] ?? 0),
            $payload['guardians']
        )));
        $finance = $this->finance->loadFinancialsForParents($parentIds);
        $payload['invoices'] = $finance['invoices'];
        $payload['payments'] = $finance['payments'];
        $payload['finance_summary'] = $finance['summary'];
        $payload['invoice_map'] = $finance['invoice_map'];

        return $payload;
    }

    private function studentsForFamily(int $familyId, string $schoolYear): array
    {
        $rows = DB::table('family_students as fs')
            ->join('students as s', 's.id', '=', 'fs.student_id')
            ->where('fs.family_id', $familyId)
            ->orderBy('s.lastname')
            ->orderBy('s.firstname')
            ->select('s.id', 's.firstname', 's.lastname')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();

        foreach ($rows as &$row) {
            $sid = (int) ($row['id'] ?? 0);
            $row['grade'] = $sid ? (string) (StudentClass::getClassSectionsByStudentId($sid, $schoolYear) ?? '') : '';
        }
        unset($row);

        return $rows;
    }

    private function emergencyContactsForParents(array $guardians): array
    {
        $parentIds = array_values(array_filter(array_map(
            static fn ($g) => (int) ($g['user_id'] ?? 0),
            $guardians
        )));

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

        $guardianMap = [];
        foreach ($guardians as $g) {
            $gid = (int) ($g['user_id'] ?? 0);
            if ($gid > 0) {
                $guardianMap[$gid] = trim(($g['firstname'] ?? '') . ' ' . ($g['lastname'] ?? ''));
            }
        }

        $rows = DB::table('emergency_contacts')
            ->select('id', 'parent_id', 'emergency_contact_name', 'relation', 'cellphone', 'email', 'school_year', 'semester', 'created_at', 'updated_at')
            ->whereIn('parent_id', $parentIds)
            ->orderBy('updated_at', 'DESC')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();

        foreach ($rows as &$row) {
            $pid = (int) ($row['parent_id'] ?? 0);
            $row['parent_label'] = $guardianMap[$pid] ?? ('Parent #' . $pid);
        }
        unset($row);

        return $rows;
    }
}
