<?php

declare(strict_types=1);

if (!defined('DENTAL_ROOT')) {
    exit;
}

function dentalcare_none_tokens(): array
{
    return [
        '', 'none', 'no', 'nil', 'n/a', 'na', 'n.a', 'n.a.', 'not applicable', 'notapplicable', '-', '--', '---',
        'no allergy', 'no allergies', 'no known allergy', 'no known allergies', 'no known drug allergy',
        'no known drug allergies', 'nkda', 'negative', 'wala', 'wala po', 'not reported', 'not recorded'
    ];
}

function dentalcare_medical_none_key(string $value): string
{
    $value = trim(mb_strtolower($value));
    $value = preg_replace('/\s+/', ' ', $value) ?? $value;
    return trim($value);
}

function dentalcare_normalize_medical_text(mixed $value): ?string
{
    $value = trim((string)$value);
    if ($value === '') return null;
    $key = dentalcare_medical_none_key($value);
    $compact = preg_replace('/[^a-z0-9]+/', '', $key) ?? $key;
    $noneTokens = dentalcare_none_tokens();
    $noneCompacts = array_map(static fn(string $token): string => preg_replace('/[^a-z0-9]+/', '', dentalcare_medical_none_key($token)) ?? '', $noneTokens);
    if (in_array($key, $noneTokens, true) || in_array($compact, $noneCompacts, true)) return null;
    return $value;
}

function dentalcare_medical_display(mixed $value, string $empty = 'None recorded'): string
{
    $normalized = dentalcare_normalize_medical_text($value);
    return $normalized === null ? $empty : $normalized;
}

function dentalcare_medical_alert_line(string $label, mixed $value): ?string
{
    $normalized = dentalcare_normalize_medical_text($value);
    return $normalized === null ? null : $label . ': ' . $normalized;
}

function dentalcare_default_service_rows(): array
{
    $dentist = [
        ['Dental Examination','Complete dental examination and consultation.',30,'consultation','public'],
        ['Comprehensive Oral Examination','Full oral examination with findings, risk review, and treatment recommendations.',45,'consultation','public'],
        ['Emergency Dental Visit','Urgent dental assessment for pain, swelling, trauma, or bleeding.',60,'emergency_dental_care','public'],
        ['Oral Prophylaxis / Dental Cleaning','Scaling and polishing for routine oral prophylaxis.',45,'oral_prophylaxis','public'],
        ['Scaling and Root Planing','Periodontal deep cleaning by quadrant or site.',60,'periodontal_scaling','reception'],
        ['Fluoride Treatment','Topical fluoride application for caries prevention.',20,'preventive_care','public'],
        ['Pit and Fissure Sealant','Sealant placement for susceptible pits and fissures.',30,'preventive_care','public'],
        ['Tooth Restoration - Composite Filling','Direct tooth-colored restoration.',60,'restoration','public'],
        ['Temporary Filling','Interim restoration or sedative dressing.',30,'restoration','reception'],
        ['Tooth Extraction - Simple','Routine non-surgical extraction.',60,'extraction','public'],
        ['Surgical Extraction','Surgical removal of tooth or retained root.',90,'oral_surgery','reception'],
        ['Wisdom Tooth Consultation','Assessment for third molar or impacted tooth management.',45,'consultation','public'],
        ['Root Canal Consultation','Endodontic evaluation and treatment planning.',60,'endodontics','public'],
        ['Root Canal Treatment','Endodontic treatment session.',90,'endodontics','reception'],
        ['Pulp Therapy / Emergency Endodontic Relief','Emergency pulpal treatment or pain-relief procedure.',60,'endodontics','reception'],
        ['Post-operative Checkup','Review of healing after extraction, surgery, or other dental procedure.',20,'consultation','reception'],
        ['Suture Removal','Removal of sutures and wound review.',20,'oral_surgery','reception'],
        ['Denture Consultation','Assessment for complete or partial denture options.',45,'prosthodontics','public'],
        ['Complete Denture','Complete denture clinical appointment.',60,'prosthodontics','reception'],
        ['Removable Partial Denture','Partial denture clinical appointment.',60,'prosthodontics','reception'],
        ['Denture Repair / Adjustment','Denture adjustment, repair assessment, or sore-spot relief.',30,'prosthodontics','reception'],
        ['Crown and Bridge Consultation','Assessment for crowns, bridges, and fixed prosthodontic options.',45,'prosthodontics','public'],
        ['Dental Crown','Crown preparation, fitting, or related crown visit.',90,'prosthodontics','reception'],
        ['Fixed Bridge','Bridge preparation, fitting, or related bridge visit.',90,'prosthodontics','reception'],
        ['Veneer Consultation','Assessment for veneers or esthetic restorations.',45,'esthetic_dentistry','public'],
        ['Tooth Whitening Consultation','Whitening assessment and shade review.',30,'esthetic_dentistry','public'],
        ['Implant Consultation','Initial implant assessment and referral/treatment planning.',45,'implant_consultation','public'],
        ['Orthodontic Consultation','Braces or aligner assessment.',45,'orthodontics','public'],
        ['Braces Adjustment','Routine orthodontic adjustment visit.',30,'orthodontics','reception'],
        ['Retainer Check','Retainer review, adjustment, or replacement assessment.',20,'orthodontics','reception'],
        ['Pediatric Dental Checkup','Child dental examination and prevention visit.',30,'pediatric_dentistry','public'],
        ['Pediatric Fluoride / Sealant','Child preventive fluoride or sealant visit.',30,'pediatric_dentistry','public'],
        ['Periodontal Consultation','Gum/periodontal assessment and treatment planning.',45,'periodontics','public'],
        ['TMJ / Occlusion Consultation','Jaw joint, bite, bruxism, or occlusion assessment.',45,'consultation','public'],
    ];
    $inhouse = [
        ['Intraoral X-ray Capture','Intraoral radiograph capture by authorized in-house staff.',20,'dental_assistant','dentist_order',0,1],
        ['Periapical X-ray Capture','Periapical radiograph capture by authorized in-house staff.',20,'dental_assistant','dentist_order',0,1],
        ['Panoramic X-ray Capture','Panoramic radiograph capture by authorized in-house staff.',25,'dental_assistant','dentist_order',0,1],
        ['Dental Photography','Clinical photo capture for documentation and treatment planning.',15,'dental_assistant','dentist_order',0,1],
        ['Patient Education / Oral Hygiene Instruction','Oral hygiene instruction, post-op instructions, or appliance-care education.',15,'dental_assistant','reception',0,0],
        ['Chair Preparation','Operatory preparation before a scheduled clinical procedure.',10,'dental_assistant','inhouse_only',0,1],
        ['Instrument Pack Release','Sterile pack release and chairside handoff for a visit.',10,'dental_assistant','inhouse_only',0,0],
        ['Sterilization Release Verification','Sterilization load/pack verification before chairside use.',10,'dental_assistant','inhouse_only',0,0],
        ['Insurance / HMO Document Intake','Administrative document intake for insurance/HMO review.',15,'staff','reception',0,0],
    ];
    $rows = [];
    foreach ($dentist as [$name,$description,$duration,$privilege,$scope]) {
        $rows[] = [
            'name'=>$name,'description'=>$description,'duration'=>$duration,'provider_type'=>'dentist','provider_role_code'=>'dentist',
            'required_privilege_code'=>$privilege,'slot_capacity'=>1,'requires_chair'=>1,'is_public'=>$scope==='public'?1:0,
            'booking_scope'=>$scope,'standard_fee'=>0.0,'fee_status'=>'unconfigured','no_charge_reason'=>null,
        ];
    }
    foreach ($inhouse as [$name,$description,$duration,$role,$scope,$isPublic,$requiresChair]) {
        $rows[] = [
            'name'=>$name,'description'=>$description,'duration'=>$duration,'provider_type'=>'inhouse','provider_role_code'=>$role,
            'required_privilege_code'=>null,'slot_capacity'=>1,'requires_chair'=>$requiresChair,'is_public'=>$isPublic,
            'booking_scope'=>$scope,'standard_fee'=>0.0,'fee_status'=>'no_charge','no_charge_reason'=>'Internal clinic support service; no direct patient charge unless configured by the clinic.',
        ];
    }
    return $rows;
}

function dentalcare_seed_default_services(PDO $pdo): int
{
    if (!schema_table_exists(table('service_catalog'))) return 0;
    $serviceTable = table('service_catalog');
    $hasFee = schema_column_exists($serviceTable, 'fee_status');
    $hasPrivilege = schema_column_exists($serviceTable, 'required_privilege_code');
    $hasScope = schema_column_exists($serviceTable, 'booking_scope');
    $stmt = $pdo->prepare(
        'SELECT id FROM `' . $serviceTable . '` WHERE LOWER(TRIM(name))=LOWER(TRIM(?)) AND provider_type=? AND (branch_id IS NULL OR branch_id=?) ORDER BY branch_id IS NULL DESC,id LIMIT 1'
    );
    $insertColumns = ['branch_id','name','description','duration_minutes','standard_fee','preparation_minutes','cleanup_minutes','provider_type','provider_role_code','slot_capacity','requires_chair','is_public','is_active','created_at'];
    if ($hasFee) array_splice($insertColumns, 5, 0, ['fee_status','no_charge_reason','fee_authorized_by','fee_authorized_at']);
    if ($hasPrivilege) $insertColumns[] = 'required_privilege_code';
    if ($hasScope) $insertColumns[] = 'booking_scope';
    $placeholders = [];
    foreach ($insertColumns as $column) $placeholders[] = $column === 'created_at' || $column === 'fee_authorized_at' ? 'NOW()' : '?';
    $insert = $pdo->prepare('INSERT INTO `' . $serviceTable . '` (`' . implode('`,`',$insertColumns) . '`) VALUES (' . implode(',',$placeholders) . ')');
    $updateSets = ['description=COALESCE(NULLIF(description,""),?)','duration_minutes=IF(duration_minutes<5,?,duration_minutes)','provider_type=?','provider_role_code=COALESCE(provider_role_code,?)','slot_capacity=GREATEST(slot_capacity,?)','requires_chair=?','is_active=1','updated_at=NOW()'];
    if ($hasPrivilege) $updateSets[] = 'required_privilege_code=COALESCE(required_privilege_code,?)';
    if ($hasScope) $updateSets[] = 'booking_scope=CASE WHEN booking_scope IS NULL OR booking_scope="" THEN ? ELSE booking_scope END,is_public=CASE WHEN booking_scope IS NULL OR booking_scope="" THEN ? ELSE is_public END';
    if ($hasFee) $updateSets[] = 'fee_status=CASE WHEN fee_status IS NULL OR fee_status="unconfigured" THEN ? ELSE fee_status END,no_charge_reason=COALESCE(no_charge_reason,?)';
    $update = $pdo->prepare('UPDATE `' . $serviceTable . '` SET ' . implode(',',$updateSets) . ' WHERE id=?');
    $count = 0;
    foreach (dentalcare_default_service_rows() as $row) {
        $stmt->execute([$row['name'],$row['provider_type'],0]);
        $id = (int)($stmt->fetchColumn() ?: 0);
        if ($id > 0) {
            $args = [$row['description'],$row['duration'],$row['provider_type'],$row['provider_role_code'],$row['slot_capacity'],$row['requires_chair']];
            if ($hasPrivilege) $args[] = $row['required_privilege_code'];
            if ($hasScope) { $args[] = $row['booking_scope']; $args[] = $row['is_public']; }
            if ($hasFee) { $args[] = $row['fee_status']; $args[] = $row['no_charge_reason']; }
            $args[] = $id;
            $update->execute($args);
            continue;
        }
        $values = [];
        foreach ($insertColumns as $column) {
            if ($column === 'created_at' || $column === 'fee_authorized_at') continue;
            $values[] = match ($column) {
                'branch_id' => null,
                'name' => $row['name'],
                'description' => $row['description'],
                'duration_minutes' => $row['duration'],
                'standard_fee' => $row['standard_fee'],
                'preparation_minutes','cleanup_minutes' => 0,
                'provider_type' => $row['provider_type'],
                'provider_role_code' => $row['provider_role_code'],
                'slot_capacity' => $row['slot_capacity'],
                'requires_chair' => $row['requires_chair'],
                'is_public' => $row['is_public'],
                'is_active' => 1,
                'fee_status' => $row['fee_status'],
                'no_charge_reason' => $row['no_charge_reason'],
                'fee_authorized_by' => null,
                'required_privilege_code' => $row['required_privilege_code'],
                'booking_scope' => $row['booking_scope'],
                default => null,
            };
        }
        $insert->execute($values);
        $count++;
    }
    return $count;
}

function dentalcare_default_facility_rows(): array
{
    return [
        ['C01','Chair 1','Operatory 1','operatory','Primary dental operatory.'],
        ['C02','Chair 2','Operatory 2','operatory','Secondary dental operatory.'],
        ['SUR','Surgery / Extraction Room','Surgery Area','surgery','Resource for surgical extraction or invasive procedures.'],
        ['XRY','X-ray / Imaging Room','Imaging Area','xray','Radiography and clinical imaging resource.'],
        ['CON','Consultation Room','Consultation Area','consultation','Consultation or case-presentation room.'],
        ['VIT','Vitals Station','In-house Screening Area','shared_inhouse','Shared in-house screening and vital-signs station.'],
    ];
}

function dentalcare_seed_default_facilities(PDO $pdo): int
{
    if (!schema_table_exists(table('clinic_chairs')) || !schema_table_exists(table('branches'))) return 0;
    $branches = $pdo->query('SELECT id FROM `' . table('branches') . '` WHERE is_active=1 ORDER BY id')->fetchAll(PDO::FETCH_COLUMN) ?: [];
    if (!$branches) return 0;
    $chairTable = table('clinic_chairs');
    $hasKind = schema_column_exists($chairTable,'resource_kind');
    $hasDefaultProvider = schema_column_exists($chairTable,'default_provider_user_id');
    $hasStatusNote = schema_column_exists($chairTable,'current_status_note');
    $hasServiceScope = schema_column_exists($chairTable,'service_scope_json');

    // A live clinic may already have Chair 1/Chair 2 from older updates, usually
    // under legacy codes such as LEGACY-*.  The default seeder must therefore
    // match by code OR name before inserting; otherwise the unique
    // (branch_id, chair_name) key throws duplicate-entry errors.
    $lookupByCodeOrName = $pdo->prepare(
        'SELECT id,chair_code,chair_name,status FROM `' . $chairTable . '` '
        . 'WHERE branch_id=? AND (LOWER(TRIM(chair_code))=LOWER(TRIM(?)) OR LOWER(TRIM(chair_name))=LOWER(TRIM(?))) '
        . 'ORDER BY CASE WHEN LOWER(TRIM(chair_name))=LOWER(TRIM(?)) THEN 0 ELSE 1 END,id LIMIT 1'
    );

    $updateSets = ['room_name=COALESCE(NULLIF(room_name,""),?)','usage_mode=CASE WHEN usage_mode IS NULL OR usage_mode="" OR usage_mode="dentist_bound" THEN ? ELSE usage_mode END','notes=COALESCE(NULLIF(notes,""),?)','updated_at=NOW()'];
    if ($hasKind) $updateSets[] = 'resource_kind=CASE WHEN resource_kind IS NULL OR resource_kind="" THEN ? ELSE resource_kind END';
    if ($hasStatusNote) $updateSets[] = 'current_status_note=COALESCE(NULLIF(current_status_note,""),?)';
    if ($hasServiceScope) $updateSets[] = 'service_scope_json=COALESCE(service_scope_json,?)';
    $update = $pdo->prepare('UPDATE `' . $chairTable . '` SET ' . implode(',',$updateSets) . ' WHERE id=?');

    $columns = ['branch_id','chair_code','chair_name','room_name','usage_mode','status','notes','created_at'];
    if ($hasKind) $columns[] = 'resource_kind';
    if ($hasDefaultProvider) $columns[] = 'default_provider_user_id';
    if ($hasStatusNote) $columns[] = 'current_status_note';
    if ($hasServiceScope) $columns[] = 'service_scope_json';
    $placeholders = [];
    foreach ($columns as $column) $placeholders[] = $column === 'created_at' ? 'NOW()' : '?';
    $insert = $pdo->prepare('INSERT INTO `' . $chairTable . '` (`' . implode('`,`',$columns) . '`) VALUES (' . implode(',',$placeholders) . ')');
    $count = 0;
    foreach ($branches as $branchId) {
        foreach (dentalcare_default_facility_rows() as [$code,$name,$room,$kind,$note]) {
            $lookupByCodeOrName->execute([(int)$branchId,$code,$name,$name]);
            $existing = $lookupByCodeOrName->fetch(PDO::FETCH_ASSOC) ?: null;
            if ($existing) {
                $args = [$room,$kind,$note];
                if ($hasKind) $args[] = $kind;
                if ($hasStatusNote) $args[] = $note;
                if ($hasServiceScope) $args[] = null;
                $args[] = (int)$existing['id'];
                $update->execute($args);
                continue;
            }
            $values = [];
            foreach ($columns as $column) {
                if ($column === 'created_at') continue;
                $values[] = match ($column) {
                    'branch_id' => (int)$branchId,
                    'chair_code' => $code,
                    'chair_name' => $name,
                    'room_name' => $room,
                    'usage_mode' => $kind,
                    'status' => 'available',
                    'notes','current_status_note' => $note,
                    'resource_kind' => $kind,
                    'default_provider_user_id' => null,
                    'service_scope_json' => null,
                    default => null,
                };
            }
            try {
                $insert->execute($values);
                $count++;
            } catch (PDOException $exception) {
                // Final guard for older MySQL hosts/collations: if a unique
                // duplicate is detected after lookup, do not fail the update.
                if (($exception->errorInfo[1] ?? null) !== 1062) {
                    throw $exception;
                }
                error_log('Default chair seed skipped duplicate for branch ' . (int)$branchId . ' / ' . $name . ': ' . $exception->getMessage());
            }
        }
    }
    return $count;
}

function dentalcare_repair_default_facility_seed_conflicts(PDO $pdo): int
{
    // Running the fixed seeder is the repair: it now treats same branch+name as
    // already-existing even when older chair_code values differ.
    return dentalcare_seed_default_facilities($pdo);
}

function dentalcare_clinic_defaults_ready(PDO $pdo): bool
{
    try {
        if (!schema_table_exists(table('service_catalog'))) return false;
        $serviceTable = table('service_catalog');
        $essentialServices = [
            'Dental Examination',
            'Emergency Dental Visit',
            'Oral Prophylaxis / Cleaning',
            'Tooth Restoration',
            'Simple Tooth Extraction',
            'Root Canal Consultation',
            'X-ray Capture',
        ];
        $found = 0;
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM `' . $serviceTable . '` WHERE LOWER(TRIM(name))=LOWER(TRIM(?)) AND is_active=1');
        foreach ($essentialServices as $name) {
            $stmt->execute([$name]);
            if ((int)$stmt->fetchColumn() > 0) $found++;
        }
        $facilityReady = true;
        if (schema_table_exists(table('clinic_chairs'))) {
            $facilityReady = (int)$pdo->query('SELECT COUNT(*) FROM `' . table('clinic_chairs') . '` WHERE status<>"inactive"')->fetchColumn() > 0;
        }
        // Do not keep System Setup in a permanent pending-update loop just because
        // an existing clinic customized/renamed optional services. Require the
        // operational essentials and the protected facility seed instead of a
        // brittle total-row count.
        return $found >= 8 && $facilityReady;
    } catch (Throwable) {
        return false;
    }
}
