<?php

declare(strict_types=1);

namespace App\Repositories;

final class StudentRepository extends BaseRepository
{
    public function allForTenant(int $tenantId): array
    {
        $statement = $this->db->prepare(
            'SELECT s.*, t.code AS template_code, t.name AS template_name, t.is_combination AS template_is_combination
             FROM students s
             JOIN license_class_templates t ON t.id = s.template_id
             WHERE s.tenant_id = :tenant_id
             ORDER BY s.last_name, s.first_name'
        );
        $statement->execute(['tenant_id' => $tenantId]);

        return $statement->fetchAll();
    }

    public function find(int $tenantId, int $id): ?array
    {
        $statement = $this->db->prepare(
            'SELECT s.*, t.code AS template_code, t.name AS template_name, t.is_combination AS template_is_combination
             FROM students s
             JOIN license_class_templates t ON t.id = s.template_id
             WHERE s.tenant_id = :tenant_id AND s.id = :id'
        );
        $statement->execute(['tenant_id' => $tenantId, 'id' => $id]);
        $student = $statement->fetch() ?: null;

        if ($student !== null) {
            $student['requirements'] = $this->requirements($id);
        }

        return $student;
    }

    public function create(int $tenantId, array $data): array
    {
        $timestamp = $this->now();
        $statement = $this->db->prepare(
            'INSERT INTO students
            (tenant_id, template_id, first_name, last_name, phone, email, needs_glasses, notes, status, created_at, updated_at)
            VALUES (:tenant_id, :template_id, :first_name, :last_name, :phone, :email, :needs_glasses, :notes, :status, :created_at, :updated_at)'
        );
        $statement->execute([
            'tenant_id' => $tenantId,
            'template_id' => $data['template_id'],
            'first_name' => $data['first_name'],
            'last_name' => $data['last_name'],
            'phone' => $data['phone'] ?? '',
            'email' => $data['email'] ?? '',
            'needs_glasses' => !empty($data['needs_glasses']) ? 1 : 0,
            'notes' => $data['notes'] ?? '',
            'status' => $data['status'] ?? 'active',
            'created_at' => $timestamp,
            'updated_at' => $timestamp,
        ]);

        $studentId = (int) $this->lastInsertId();
        $this->snapshotRequirements($studentId, (int) $data['template_id']);

        return $this->find($tenantId, $studentId);
    }

    public function update(int $tenantId, int $id, array $data): ?array
    {
        $statement = $this->db->prepare(
            'UPDATE students
             SET template_id = :template_id, first_name = :first_name, last_name = :last_name, phone = :phone, email = :email, needs_glasses = :needs_glasses,
                 notes = :notes, status = :status, updated_at = :updated_at
             WHERE tenant_id = :tenant_id AND id = :id'
        );
        $statement->execute([
            'tenant_id' => $tenantId,
            'id' => $id,
            'template_id' => $data['template_id'],
            'first_name' => $data['first_name'],
            'last_name' => $data['last_name'],
            'phone' => $data['phone'] ?? '',
            'email' => $data['email'] ?? '',
            'needs_glasses' => !empty($data['needs_glasses']) ? 1 : 0,
            'notes' => $data['notes'] ?? '',
            'status' => $data['status'] ?? 'active',
            'updated_at' => $this->now(),
        ]);

        return $this->find($tenantId, $id);
    }

    public function delete(int $tenantId, int $id): bool
    {
        $student = $this->find($tenantId, $id);
        if ($student === null) {
            return false;
        }

        $this->db->beginTransaction();
        try {
            $updateAppointments = $this->db->prepare(
                'UPDATE appointments SET student_id = NULL, updated_at = :updated_at WHERE tenant_id = :tenant_id AND student_id = :student_id'
            );
            $updateAppointments->execute([
                'tenant_id' => $tenantId,
                'student_id' => $id,
                'updated_at' => $this->now(),
            ]);

            $deleteSnapshots = $this->db->prepare('DELETE FROM student_requirement_snapshots WHERE student_id = :student_id');
            $deleteSnapshots->execute(['student_id' => $id]);

            $deleteStudent = $this->db->prepare('DELETE FROM students WHERE tenant_id = :tenant_id AND id = :id');
            $deleteStudent->execute(['tenant_id' => $tenantId, 'id' => $id]);

            $this->db->commit();
            return true;
        } catch (\Throwable $exception) {
            $this->db->rollBack();
            throw $exception;
        }
    }

    public function requirements(int $studentId): array
    {
        $this->syncRequirementSnapshots($studentId);

        $statement = $this->db->prepare(
            'SELECT * FROM student_requirement_snapshots WHERE student_id = :student_id ORDER BY sort_order, phase_label, label'
        );
        $statement->execute(['student_id' => $studentId]);

        return $statement->fetchAll();
    }

    public function updatePriorCompletedUnits(int $tenantId, int $studentId, array $rows): ?array
    {
        $student = $this->find($tenantId, $studentId);
        if ($student === null) {
            return null;
        }

        $statement = $this->db->prepare(
            'UPDATE student_requirement_snapshots
             SET prior_completed_units = :prior_completed_units
             WHERE student_id = :student_id AND requirement_key = :requirement_key'
        );

        foreach ($rows as $row) {
            $statement->execute([
                'student_id' => $studentId,
                'requirement_key' => (string) ($row['requirement_key'] ?? ''),
                'prior_completed_units' => max(0, (int) ($row['prior_completed_units'] ?? 0)),
            ]);
        }

        return $this->find($tenantId, $studentId);
    }

    private function snapshotRequirements(int $studentId, int $templateId): void
    {
        $this->syncRequirementSnapshots($studentId, $templateId);
    }

    private function syncRequirementSnapshots(int $studentId, ?int $templateId = null): void
    {
        if ($templateId === null) {
            $student = $this->db->prepare('SELECT template_id FROM students WHERE id = :student_id');
            $student->execute(['student_id' => $studentId]);
            $templateId = (int) ($student->fetch()['template_id'] ?? 0);
        }

        if ($templateId <= 0) {
            return;
        }

        $requirements = $this->db->prepare(
            'SELECT requirement_key, label, required_units, sort_order, phase_label
             FROM license_template_requirements WHERE template_id = :template_id'
        );
        $requirements->execute(['template_id' => $templateId]);
        $rows = [];

        foreach ($requirements->fetchAll() as $row) {
            $rows[$row['requirement_key']] = $row;
        }
        $hasPhasedRequirements = array_reduce(
            $rows,
            static fn (bool $carry, array $row): bool => $carry || trim((string) ($row['phase_label'] ?? '')) !== '',
            false
        );

        $lessonTypes = $this->db->prepare(
            'SELECT COALESCE(NULLIF(TRIM(lt.requirement_key), \'\'), \'lesson_type_\' || lt.id) AS requirement_key,
                    lt.name AS label,
                    lt.sort_order
             FROM lesson_types lt
             JOIN lesson_type_template_visibility vis ON vis.lesson_type_id = lt.id
             WHERE vis.template_id = :template_id
               AND lt.category = \'student\'
               AND lt.is_counted = 1
             ORDER BY lt.sort_order, lt.name'
        );
        $lessonTypes->execute(['template_id' => $templateId]);

        foreach ($lessonTypes->fetchAll() as $row) {
            if (isset($rows[$row['requirement_key']])) {
                continue;
            }

            if ($hasPhasedRequirements) {
                $hasMatchingPhasedRequirement = false;
                foreach ($rows as $existingRow) {
                    $phaseLabel = trim((string) ($existingRow['phase_label'] ?? ''));
                    if ($phaseLabel === '') {
                        continue;
                    }

                    if (str_ends_with((string) $existingRow['requirement_key'], '::' . $row['requirement_key'])) {
                        $hasMatchingPhasedRequirement = true;
                        break;
                    }
                }

                if ($hasMatchingPhasedRequirement) {
                    continue;
                }
            }

            $rows[$row['requirement_key']] = [
                'requirement_key' => $row['requirement_key'],
                'label' => $row['label'],
                'required_units' => 0,
                'sort_order' => $row['sort_order'],
            ];
        }

        $insert = $this->db->prepare(
            'INSERT INTO student_requirement_snapshots
             (student_id, requirement_key, label, required_units, prior_completed_units, sort_order, phase_label)
             SELECT :student_id, :requirement_key, :label, :required_units, 0, :sort_order, :phase_label
             WHERE NOT EXISTS (
                SELECT 1 FROM student_requirement_snapshots
                WHERE student_id = :student_id AND requirement_key = :requirement_key
             )'
        );
        $update = $this->db->prepare(
            'UPDATE student_requirement_snapshots
             SET label = :label,
                 required_units = :required_units,
                 sort_order = :sort_order,
                 phase_label = :phase_label
             WHERE student_id = :student_id AND requirement_key = :requirement_key'
        );

        foreach ($rows as $row) {
            $insert->execute([
                'student_id' => $studentId,
                'requirement_key' => $row['requirement_key'],
                'label' => $row['label'],
                'required_units' => $row['required_units'],
                'sort_order' => $row['sort_order'],
                'phase_label' => $row['phase_label'] ?? '',
            ]);
            $update->execute([
                'student_id' => $studentId,
                'requirement_key' => $row['requirement_key'],
                'label' => $row['label'],
                'required_units' => $row['required_units'],
                'sort_order' => $row['sort_order'],
                'phase_label' => $row['phase_label'] ?? '',
            ]);
        }

        if ($rows !== []) {
            $placeholders = implode(', ', array_fill(0, count($rows), '?'));
            $delete = $this->db->prepare(
                "DELETE FROM student_requirement_snapshots
                 WHERE student_id = ?
                   AND requirement_key NOT IN ($placeholders)
                   AND prior_completed_units = 0
                   AND NOT EXISTS (
                       SELECT 1 FROM appointment_units au
                       JOIN appointments a ON a.id = au.appointment_id
                       WHERE a.student_id = student_requirement_snapshots.student_id
                         AND au.requirement_key = student_requirement_snapshots.requirement_key
                   )"
            );
            $delete->execute(array_merge([$studentId], array_keys($rows)));
        }
    }
}
