<?php
/**
 * Coding view (coding.work).
 *   ?next=1        -> claim the oldest pending chart (FIFO, TOP priority first) and open it
 *   ?next=returned -> claim the oldest chart SENT BACK to this coder (rework) and open it
 *   ?id=X     -> open a specific chart for coding / resume
 *   POST save -> demographics + CPT/ICD/modifier lines + submit status
 */
require_once __DIR__ . '/../includes/config.php';
$user = require_login('internal');
$pdo  = db();
$uid  = (int) $user['id'];

function chart_age(?string $dob, ?string $dos): ?int {
    if (!$dob || !$dos) return null;
    $a = date_create($dob); $b = date_create($dos);
    return ($a && $b) ? $b->diff($a)->y : null;
}

/* ---------------- GET ?next=1 / ?next=returned : claim the next chart ---------------- */
$nextMode = $_GET['next'] ?? '';
if ($nextMode === '1' || $nextMode === 'returned') {
    $onlyReturned = ($nextMode === 'returned');
    $nextId = coding_next_chart_id($pdo, $uid, $onlyReturned);
    if (!$nextId) {
        flash('error', $onlyReturned ? 'No charts have been sent back to you to rework.' : 'Your open queue is empty — nothing to code.');
        redirect('/admin/coding-inventory.php' . ($onlyReturned ? '?returned=1' : ''));
    }
    $pdo->prepare("UPDATE coding_inventory SET status='in_progress', started_at=COALESCE(started_at,NOW()), updated_at=NOW() WHERE id=? AND status='pending'")->execute([$nextId]);
    redirect('/admin/coding-chart.php?id=' . $nextId);
}

/* ---------------- POST action=new_manual_chart : coder creates a chart by hand (Excel-inventory clients) ----------------
 * Pick client » process » facility » billing provider on My Inventory → we create a blank in_progress chart and open it. */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'new_manual_chart') {
    csrf_check_or_die();
    if (!can('coding.work')) { http_response_code(403); exit('No coding access.'); }
    $cid = (int) input('client_id'); $pid = (int) input('process_id'); $fid = (int) input('facility_id'); $prid = (int) input('provider_id');
    if (!$cid || !$pid || !$fid || !$prid || !coding_user_can_tuple($uid, $cid, $pid, $fid, $prid)) {
        flash('error', 'Pick a client, process, facility and billing provider that are in your access.'); redirect('/admin/coding-inventory.php');
    }
    $newId = coding_create_manual_chart($pdo, $uid, $cid, $pid, $fid, $prid);
    audit_log('coding.chart.manual_create', 'coding_inventory', $newId, null, ['client'=>$cid,'process'=>$pid,'facility'=>$fid,'provider'=>$prid]);
    redirect('/admin/coding-chart.php?id=' . $newId);
}

$id = (int) ($_GET['id'] ?? $_POST['id'] ?? 0);
if (!$id) { redirect('/admin/coding-inventory.php'); }

/* Load the chart + context. */
$chart = $pdo->prepare("SELECT ci.*, c.name cn, p.name pn, f.name fn, pr.name uploaded_provider
    FROM coding_inventory ci
    JOIN clients c ON c.id=ci.client_id JOIN processes p ON p.id=ci.process_id
    JOIN coding_facilities f ON f.id=ci.facility_id JOIN coding_providers pr ON pr.id=ci.provider_id
    WHERE ci.id=?");
$chart->execute([$id]);
$chart = $chart->fetch();
if (!$chart) { flash('error', 'Chart not found.'); redirect('/admin/coding-inventory.php'); }

$isOwner = ((int) $chart['assigned_to'] === $uid);
// Coders open their own; supervisors open their team's; auditors may open any chart (to review and, in the
// post-agreement window, correct it). Edit rights are still gated separately below ($editable).
if (!$isOwner && !can('coding.supervise') && !can('coding.audit')) { flash('error', 'That chart is not assigned to you.'); redirect('/admin/coding-inventory.php'); }

// Opening / resuming a pending or held chart makes it the coder's active in-progress one.
// A coder works one chart at a time, so any other active chart is parked on Hold.
if ($isOwner && in_array($chart['status'], ['pending','hold'], true) && $_SERVER['REQUEST_METHOD'] !== 'POST') {
    $pdo->prepare("UPDATE coding_inventory SET status='hold', updated_at=NOW() WHERE assigned_to=? AND status='in_progress' AND id<>?")->execute([$uid, $id]);
    $pdo->prepare("UPDATE coding_inventory SET status='in_progress', started_at=COALESCE(started_at,NOW()), updated_at=NOW() WHERE id=?")->execute([$id]);
    $chart['status'] = 'in_progress';
}

$editable = in_array($chart['status'], ['pending','in_progress','hold'], true);
$codingState = $editable;   // TRUE only for an ACTIVELY-CODED chart — facility change is limited to this (not QA/supervisor edits of completed charts)
// Pre-audit review: a supervisor / manager may also edit a TEAM member's COMPLETED chart that has NOT yet been
// audited (audit_status NULL) and not released to billing — fixes before the auditor picks it up. Reuses this
// whole editor + the draft-save path (keeps status='completed', so the chart stays in the audit queue).
if (!$editable && $chart['status'] === 'completed' && empty($chart['audit_status']) && empty($chart['released_to_billing_at']) && can('coding.supervise')) {
    $revTeam = coding_team_scope($pdo, $uid);   // null = sysadmin (everyone)
    if ($revTeam === null || in_array((int) ($chart['coder_id'] ?: $chart['assigned_to']), $revTeam, true)) $editable = true;
}
// Pre-release edit of an AUDITED chart: once a chart is audited (audit_status='ready_to_deliver') but NOT yet
// released to billing, an AUDITOR (any) or a SUPERVISOR/manager (their team) may still edit it — e.g. the classic
// post-rebuttal "coder agreed to an error → auditor corrects the codes", but ALSO any final fix before release
// (the user's ask: an audited-but-not-submitted chart must be editable, not locked). Uses the QA-edit save path:
// the coder's credit + sampling are preserved, the chart stays completed/ready_to_deliver (still deliverable),
// and the prior per-code audit findings are SUPERSEDED so billing bills the corrected codes without double-applying.
$qaEdit = false;
if (!$editable && $chart['status'] === 'completed'
    && ($chart['audit_status'] ?? '') === 'ready_to_deliver'
    && empty($chart['released_to_billing_at'])) {
    if (can('coding.audit')) {
        // A plain auditor is fenced to their assigned clients (coding_user_scope), matching coding-audit.php's client
        // fence and the during-audit gate below — sysadmin / supervisor are unrestricted. (Prevents a cross-client
        // PHI edit: without this an auditor aligned to client A could edit an audited chart of client B.)
        $ok = coding_scope_unrestricted($uid);
        if (!$ok) {
            $cs = $pdo->prepare("SELECT 1 FROM coding_user_scope WHERE user_id=? AND client_id=? AND " . coding_vertical_scope_sql('vertical_id') . " LIMIT 1");
            $cs->execute([$uid, (int) $chart['client_id']]); $ok = (bool) $cs->fetchColumn();
        }
        if ($ok) { $editable = true; $qaEdit = true; }
    } elseif (can('coding.supervise')) {
        $revTeam2 = coding_team_scope($pdo, $uid);   // null = sysadmin (everyone)
        if ($revTeam2 === null || in_array((int) ($chart['coder_id'] ?: $chart['assigned_to']), $revTeam2, true)) { $editable = true; $qaEdit = true; }
    }
}
// DURING-AUDIT correction (opened from the Audit page's "Edit chart" → &audit_edit=1): an AUDITOR — fenced to their
// assigned clients, mirroring coding-audit.php — or a SUPERVISOR (their team) may correct a completed, not-yet-released
// chart even while it is still being audited (audit_status null / in_progress). Same qaEdit save path (coder credit +
// sampling preserved, audit findings superseded so billing bills the corrected codes).
$auditEditReq = (($_GET['audit_edit'] ?? $_POST['audit_edit'] ?? '') === '1');
if (!$editable && $auditEditReq && $chart['status'] === 'completed' && empty($chart['released_to_billing_at'])) {
    if (can('coding.audit')) {
        $ok = is_sysadmin() || can('coding.supervise');            // unrestricted per coding_scope_unrestricted
        if (!$ok) {                                                // a plain auditor is fenced to their coding_user_scope clients
            $cs = $pdo->prepare("SELECT 1 FROM coding_user_scope WHERE user_id=? AND client_id=? AND " . coding_vertical_scope_sql('vertical_id') . " LIMIT 1");
            $cs->execute([$uid, (int) $chart['client_id']]); $ok = (bool) $cs->fetchColumn();
        }
        if ($ok) { $editable = true; $qaEdit = true; }
    } elseif (can('coding.supervise')) {
        $revTeam3 = coding_team_scope($pdo, $uid);
        if ($revTeam3 === null || in_array((int) ($chart['coder_id'] ?: $chart['assigned_to']), $revTeam3, true)) { $editable = true; $qaEdit = true; }
    }
}

/* Coder changes this chart's FACILITY (within the same client»process). Facility-specific provider refs are cleared
   so the coder re-picks a provider valid at the new facility; the coded CPT lines are kept. Only while editable. */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'change_facility') {
    csrf_check_or_die();
    if (!$codingState) { flash('error', 'The facility can only be changed while the chart is being coded.'); redirect('/admin/coding-chart.php?id=' . $id); }
    $newFac = (int) input('facility_id');
    if ($newFac && $newFac !== (int) $chart['facility_id']) {
        $ok = $pdo->prepare("SELECT 1 FROM coding_facilities WHERE id=? AND process_id=? AND status='active'");
        $ok->execute([$newFac, (int) $chart['process_id']]);
        if (!$ok->fetchColumn()) { flash('error', 'Pick an active facility under this chart’s process.'); redirect('/admin/coding-chart.php?id=' . $id); }
        if (!coding_scope_unrestricted($uid) && !coding_user_can_tuple($uid, (int) $chart['client_id'], (int) $chart['process_id'], $newFac, 0)) {
            flash('error', 'You are not aligned to that facility.'); redirect('/admin/coding-chart.php?id=' . $id);
        }
        $pdo->prepare("UPDATE coding_inventory SET facility_id=?, coded_provider_id=NULL, rendering_provider_id=NULL, billing_provider_id=NULL, pcp_provider_id=NULL, updated_at=NOW() WHERE id=?")
            ->execute([$newFac, $id]);
        audit_log('coding.chart.facility_change', 'coding_inventory', $id, ['facility' => (int) $chart['facility_id']], ['facility' => $newFac]);
        flash('success', 'Facility changed — re-select the provider for the new facility.');
    }
    redirect('/admin/coding-chart.php?id=' . $id);
}

/* Facilities the coder may switch this chart to — active facilities under the SAME client»process, within the coder's
   scope. The current facility is always included so it stays selectable. */
$facOptions = [];
if ($codingState) {
    $fo = $pdo->prepare("SELECT id, name FROM coding_facilities WHERE process_id=? AND status='active' ORDER BY name");
    $fo->execute([(int) $chart['process_id']]);
    $unrestricted = coding_scope_unrestricted($uid);
    foreach ($fo->fetchAll() as $f) {
        if ($unrestricted || (int) $f['id'] === (int) $chart['facility_id']
            || coding_user_can_tuple($uid, (int) $chart['client_id'], (int) $chart['process_id'], (int) $f['id'], 0))
            $facOptions[] = $f;
    }
}

/* Providers available at this facility (for switch + rendering/billing/PCP selects). */
$facProviders = $pdo->prepare("SELECT pr.id, pr.name FROM coding_facility_providers fp JOIN coding_providers pr ON pr.id=fp.provider_id WHERE fp.facility_id=? AND pr.provider_type='billing' ORDER BY pr.name");
$facProviders->execute([(int) $chart['facility_id']]);
$facProviders = $facProviders->fetchAll();

/* Scribe CANDIDATES for THIS facility (CONTEXTUAL model): any active provider who is NOT a billing provider of this
   facility can scribe here — dedicated scribes + billing providers whose billing home is another facility. When
   "Scribe?" is Yes the Rendering provider field searches these. */
$scribes = coding_scribe_options($pdo, (int) $chart['facility_id']);
/* For an already-scribed chart, the saved scribe lives in rendering_provider_id — load its name so the picker
   can show it on load even if it is not in the active-scribe list. */
$savedScribe = null;
if (!empty($chart['scribe_flag']) && !empty($chart['rendering_provider_id'])) {
    $ss = $pdo->prepare("SELECT id, name FROM coding_providers WHERE id=?");
    $ss->execute([(int) $chart['rendering_provider_id']]);
    $savedScribe = $ss->fetch(PDO::FETCH_ASSOC) ?: null;
}

$codedProvider = (int) ($chart['coded_provider_id'] ?: $chart['provider_id']);

/* Billing-sheet picker (shown as a strip over the chart, like the My-inventory strip): ALL sheets uploaded for THIS
 * chart's client»process»facility — billing sheets (tagged to a billing provider) AND scribe sheets (tagged to a
 * scribe, no billing provider). The coder picks the Provider (a facility billing provider OR a scribe) + a date and
 * clicks View. Pre-selected to the chart's billing/rendering provider + DOS, but they can change either. */
$bq = $pdo->prepare("SELECT bs.id, bs.provider_id, bs.scribe_id, DATE_FORMAT(bs.sheet_date,'%Y-%m-%d') d, bs.original_name, bs.patient_count,
        COALESCE(pr.name, sc.name) prov_name, (bs.scribe_id IS NOT NULL) is_scribe
    FROM coding_billing_sheets bs
    LEFT JOIN coding_providers pr ON pr.id=bs.provider_id
    LEFT JOIN coding_providers sc ON sc.id=bs.scribe_id
    WHERE bs.client_id=? AND bs.process_id=? AND bs.facility_id=? ORDER BY bs.sheet_date DESC, prov_name");
$bq->execute([(int)$chart['client_id'], (int)$chart['process_id'], (int)$chart['facility_id']]);
$bsSheets = $bq->fetchAll(PDO::FETCH_ASSOC);
// Provider/scribe options for the strip dropdown: the facility's billing providers + scribe candidates (any active provider not billing here).
$bsProvOpts = [];
foreach ($facProviders as $fpr) $bsProvOpts[] = ['id'=>(int)$fpr['id'], 'name'=>$fpr['name'], 'type'=>'billing'];
foreach ($scribes as $sc)       $bsProvOpts[] = ['id'=>(int)$sc['id'],  'name'=>$sc['name'] . ' — Scribe', 'type'=>'scribe'];
$bsPreProv = !empty($chart['scribe_flag']) ? (int)$chart['rendering_provider_id'] : (int)($chart['billing_provider_id'] ?: $chart['provider_id']);
$bsDos = $chart['dos'] ? substr((string)$chart['dos'], 0, 10) : '';   // the chart's DOS, to default-select its sheet's date

/* ---------------- POST autosave : silently persist a draft (demographics + code lines) ----------------
 * The client posts confirmed codes as the coder enters them. No completed-validation; status is untouched. */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'autosave') {
    while (ob_get_level() > 0) ob_end_clean();          // drop any stray output so the JSON body is never corrupted
    ini_set('display_errors', '0');
    header('Content-Type: application/json; charset=utf-8');
    if (!csrf_check())  { http_response_code(419); echo json_encode(['ok' => false, 'reason' => 'csrf']); exit; }
    if (!$editable)     { echo json_encode(['ok' => false, 'reason' => 'locked']); exit; }

    $cpts = [];
    foreach ((array) ($_POST['cpt'] ?? []) as $c) {
        $code = strtoupper(trim((string) ($c['code'] ?? '')));
        if ($code === '') continue;
        $cpts[] = [
            'code'  => $code,
            'units' => max(1, (int) round((float) ($c['units'] ?? 1))),
            'dos'   => coding_parse_date($c['dos'] ?? null),
            'icds'  => array_values(array_filter(array_map(fn($x) => strtoupper(trim((string) $x)), (array) ($c['icds'] ?? [])))),
            'mods'  => array_values(array_filter(array_map(fn($x) => strtoupper(trim((string) $x)), (array) ($c['mods'] ?? [])))),
        ];
    }
    $dob = coding_parse_date(input('patient_dob'));
    $groupDos = array_values(array_filter(array_map(fn($x) => coding_parse_date($x), (array) ($_POST['group_dos'] ?? []))));
    $lineDates = array_merge(array_values(array_filter(array_map(fn($c) => $c['dos'], $cpts))), $groupDos); sort($lineDates);
    $dos = $lineDates[0] ?? null;
    $pcpFlag = input('pcp_flag') === '1' ? 1 : 0;
    $pcpName = $pcpFlag ? mb_substr(trim((string) input('pcp_provider_name')), 0, 150) : '';
    $pcpId = null;
    if ($pcpName !== '') foreach ($facProviders as $fp) if (strcasecmp((string) $fp['name'], $pcpName) === 0) { $pcpId = (int) $fp['id']; break; }
    $codedProv = (int) input('coded_provider_id') ?: (int) $chart['provider_id'];

    try {
        $pdo->beginTransaction();
        $scribeFlag = input('scribe_flag') === '1' ? 1 : 0;   // Scribe? — when set, Rendering holds a scribe
        $pdo->prepare("UPDATE coding_inventory SET
                patient_mrn=?, patient_name=?, patient_dob=?, record_sign_date=?, coded_provider_id=?,
                gender=?, dos=?, age_at_dos=?, pcp_flag=?, pcp_provider_id=?, pcp_provider_name=?,
                scribe_flag=?, rendering_provider_id=?, billing_provider_id=?, comment=?, updated_at=NOW()
            WHERE id=?")->execute([
            trim((string) input('patient_mrn')) ?: null,
            mb_substr(trim((string) input('patient_name')), 0, 200),
            $dob, coding_parse_date(input('record_sign_date')), $codedProv,
            (in_array(input('gender'), ['male','female','other','unknown'], true) ? (string) input('gender') : null),
            $dos, chart_age($dob, $dos), $pcpFlag, $pcpId, ($pcpName !== '' ? $pcpName : null),
            $scribeFlag,
            ((int) input('rendering_provider_id')) ?: null,
            (((int) input('billing_provider_id')) ?: ($scribeFlag ? 0 : (int) input('rendering_provider_id'))) ?: null,   // billing default = rendering, but NEVER the scribe
            (trim((string) input('comment')) ?: null), $id,
        ]);
        coding_save_chart_lines($pdo, $id, $cpts);
        $pdo->commit();
        echo json_encode(['ok' => true, 'cpts' => count($cpts)]);
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        http_response_code(500);
        echo json_encode(['ok' => false, 'reason' => APP_DEBUG ? $e->getMessage() : 'error']);
    }
    exit;
}

/* ---------------- POST save_draft : reliable FULL-PAGE draft save ----------------
 * Same transport as normal navigation (which works on the shared host even when background
 * fetch/XHR does not), so the coder's codes are persisted and never silently lost. No
 * completed-validation; status is left as-is. Triggered by the "Save codes" submit button. */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['draft'])) {
    csrf_check_or_die();
    if (!$editable) { flash('error', 'This chart can no longer be edited.'); redirect('/admin/coding-inventory.php'); }

    $cpts = [];
    foreach ((array) ($_POST['cpt'] ?? []) as $c) {
        $code = strtoupper(trim((string) ($c['code'] ?? '')));
        if ($code === '') continue;
        $cpts[] = [
            'code'  => $code,
            'units' => max(1, (int) round((float) ($c['units'] ?? 1))),
            'dos'   => coding_parse_date($c['dos'] ?? null),
            'icds'  => array_values(array_filter(array_map(fn($x) => strtoupper(trim((string) $x)), (array) ($c['icds'] ?? [])))),
            'mods'  => array_values(array_filter(array_map(fn($x) => strtoupper(trim((string) $x)), (array) ($c['mods'] ?? [])))),
        ];
    }
    $dob = coding_parse_date(input('patient_dob'));
    $groupDos = array_values(array_filter(array_map(fn($x) => coding_parse_date($x), (array) ($_POST['group_dos'] ?? []))));
    $lineDates = array_merge(array_values(array_filter(array_map(fn($c) => $c['dos'], $cpts))), $groupDos); sort($lineDates);
    $dos = $lineDates[0] ?? null;
    $pcpFlag = input('pcp_flag') === '1' ? 1 : 0;
    $pcpName = $pcpFlag ? mb_substr(trim((string) input('pcp_provider_name')), 0, 150) : '';
    $pcpId = null;
    if ($pcpName !== '') foreach ($facProviders as $fp) if (strcasecmp((string) $fp['name'], $pcpName) === 0) { $pcpId = (int) $fp['id']; break; }
    $codedProv = (int) input('coded_provider_id') ?: (int) $chart['provider_id'];

    $scribeFlag = input('scribe_flag') === '1' ? 1 : 0;
    $pdo->beginTransaction();
    $pdo->prepare("UPDATE coding_inventory SET
            patient_mrn=?, patient_name=?, patient_dob=?, record_sign_date=?, coded_provider_id=?,
            gender=?, dos=?, age_at_dos=?, pcp_flag=?, pcp_provider_id=?, pcp_provider_name=?,
            scribe_flag=?, rendering_provider_id=?, billing_provider_id=?, comment=?, updated_at=NOW()
        WHERE id=?")->execute([
        trim((string) input('patient_mrn')) ?: null,
        mb_substr(trim((string) input('patient_name')), 0, 200),
        $dob, coding_parse_date(input('record_sign_date')), $codedProv,
        (in_array(input('gender'), ['male','female','other','unknown'], true) ? (string) input('gender') : null),
        $dos, chart_age($dob, $dos), $pcpFlag, $pcpId, ($pcpName !== '' ? $pcpName : null),
        $scribeFlag,
        ((int) input('rendering_provider_id')) ?: null,
        (((int) input('billing_provider_id')) ?: ($scribeFlag ? 0 : (int) input('rendering_provider_id'))) ?: null,
        (trim((string) input('comment')) ?: null), $id,
    ]);
    coding_save_chart_lines($pdo, $id, $cpts);
    $pdo->commit();
    flash('success', count($cpts) . ' CPT line(s) saved as draft — your codes are kept. Submit when ready.');
    redirect('/admin/coding-chart.php?id=' . $id);
}

/* ---------------- POST save ---------------- */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save') {
    csrf_check_or_die();
    if (!$editable) { flash('error', 'This chart can no longer be edited.'); redirect('/admin/coding-inventory.php'); }

    $status = in_array(input('status'), CODING_CHART_STATUSES, true) ? (string) input('status') : 'in_progress';
    if ($qaEdit) $status = 'completed';   // a QA correction never changes the chart's state — it stays completed
    $comment = trim((string) input('comment'));
    $secondary = trim((string) input('secondary_reason'));
    $codedProv = (int) input('coded_provider_id') ?: (int) $chart['provider_id'];

    // normalize CPT lines (each line carries its own date of service)
    $cpts = [];
    foreach ((array) ($_POST['cpt'] ?? []) as $c) {
        $code = strtoupper(trim((string) ($c['code'] ?? '')));
        if ($code === '') continue;
        $cpts[] = [
            'code'  => $code,
            'units' => max(1, (int) round((float) ($c['units'] ?? 1))),
            'dos'   => coding_parse_date($c['dos'] ?? null),
            'icds'  => array_values(array_filter(array_map(fn($x) => strtoupper(trim((string) $x)), (array) ($c['icds'] ?? [])))),
            'mods'  => array_values(array_filter(array_map(fn($x) => strtoupper(trim((string) $x)), (array) ($c['mods'] ?? [])))),
        ];
    }

    // DOS across coded lines + every DOS-group header (a DOS-only group has no CPT line — the RFI/Disregard case).
    $groupDos = array_values(array_filter(array_map(fn($x) => coding_parse_date($x), (array) ($_POST['group_dos'] ?? []))));
    $allDos = array_merge(array_values(array_filter(array_map(fn($c) => $c['dos'], $cpts))), $groupDos);
    sort($allDos);
    $chartDos = $allDos[0] ?? null;

    $errors = [];
    if (in_array($status, ['rfi','disregard'], true)) {
        if ($comment === '')   $errors[] = 'A comment is required for RFI / Disregard.';
        if ($secondary === '') $errors[] = 'Select a secondary reason.';
        // Patient demographics + a date of service are mandatory to tag a chart RFI / Disregard (no codes required).
        if (trim((string) input('patient_mrn')) === '')  $errors[] = 'Patient MRN/PRN is required to tag RFI / Disregard.';
        if (trim((string) input('patient_name')) === '') $errors[] = 'Patient name is required to tag RFI / Disregard.';
        if (!coding_parse_date(input('patient_dob')))    $errors[] = 'DOB is required to tag RFI / Disregard.';
        if (!in_array(input('gender'), ['male','female','other','unknown'], true)) $errors[] = 'Gender is required to tag RFI / Disregard.';
        if (!$chartDos) $errors[] = 'A date of service (DOS) is required to tag RFI / Disregard.';
        elseif ($chartDos > date('Y-m-d')) $errors[] = "Date of service {$chartDos} can't be in the future.";
    }
    if ($status === 'completed') {
        $rsd = coding_parse_date(input('record_sign_date'));   // DOS may not be later than this — future-to-signed must go to discrepancy, not complete
        if (!$cpts) $errors[] = 'Add at least one CPT before completing.';
        foreach ($cpts as $c) {
            if (!$c['dos']) $errors[] = "Set a date of service for CPT {$c['code']}.";
            elseif ($c['dos'] > date('Y-m-d')) $errors[] = "Date of service {$c['dos']} (CPT {$c['code']}) can't be in the future.";
            elseif ($rsd && $c['dos'] > $rsd) $errors[] = "DOS {$c['dos']} (CPT {$c['code']}) is after the record sign date {$rsd} — fix the DOS, or tag the chart as a discrepancy (RFI / Disregard).";
            if (empty($c['icds'])) $errors[] = "Add at least one ICD diagnosis under CPT {$c['code']}.";   // every CPT row needs a diagnosis to complete
            if (!coding_cpt_is_valid($pdo, (int) $chart['facility_id'], $codedProv, $c['code']))
                $errors[] = "CPT {$c['code']} is not in the validation set for this provider — ask your supervisor to add it via Hierarchy.";
            // reject incomplete / unrecognised codes
            foreach ($c['icds'] as $icd) if (!coding_icd_is_valid($pdo, $icd)) $errors[] = "ICD “{$icd}” (under CPT {$c['code']}) is not a complete, valid diagnosis code.";
            foreach ($c['mods'] as $m)   if (!in_array($m, coding_modifiers($pdo), true)) $errors[] = "Modifier “{$m}” (under CPT {$c['code']}) is not a valid modifier.";
        }
        // All demographics are mandatory to complete a chart (the Scribe? toggle is optional; when Yes the
        // Rendering provider holds the scribe, so the "rendering required" check below covers it).
        if (trim((string) input('patient_mrn')) === '')          $errors[] = 'Patient MRN/PRN is required to complete.';
        if (trim((string) input('patient_name')) === '')         $errors[] = 'Patient name is required to complete.';
        if (!coding_parse_date(input('patient_dob')))            $errors[] = 'DOB is required to complete.';
        if (!in_array(input('gender'), ['male','female','other','unknown'], true)) $errors[] = 'Gender is required to complete.';
        if (!coding_parse_date(input('record_sign_date')))      $errors[] = 'Record sign date is required to complete.';
        if ((int) input('rendering_provider_id') <= 0)           $errors[] = (input('scribe_flag') === '1' ? 'Scribe is required when Scribe is set to Yes.' : 'Rendering provider is required to complete.');
        if ((int) input('billing_provider_id') <= 0)             $errors[] = 'Billing provider is required to complete.';
    }

    // ── Duplicate-encounter guard ── same patient + DOS + facility already COMPLETED. Same provider → hard-block
    // completion (re-upload after a provider edit → tag RFI instead); different provider → soft warning, allowed.
    $dupWarn = '';
    if ($status === 'completed' && $chartDos) {
        $dups = coding_duplicate_encounters($pdo, $id, (int) $chart['facility_id'], $chartDos,
            (string) input('patient_mrn'), (string) input('patient_name'), coding_parse_date(input('patient_dob')), (int) $chart['provider_id']);
        if ($dups['same']) {
            $d = $dups['same'][0];
            $errors[] = "Duplicate encounter — this patient was already CODED (completed) at this facility on {$chartDos} for the same provider"
                . ($d['provider'] ? " ({$d['provider']})" : '') . " on chart #{$d['id']}. This is most likely a re-upload after the provider edited the note. "
                . "Do not complete it again — tag this chart as RFI (discrepancy) so the validator can route it to the client.";
        } elseif ($dups['diff']) {
            $d = $dups['diff'][0];
            $dupWarn = "Note: a different provider" . ($d['provider'] ? " ({$d['provider']})" : '') . " also has this patient at this facility on {$chartDos} (chart #{$d['id']}). Coding both is allowed — this chart was submitted for audit.";
        }
    }

    if ($errors) {
        foreach ($errors as $er) flash('error', $er);
        redirect('/admin/coding-chart.php?id=' . $id);
    }

    $dob  = coding_parse_date(input('patient_dob'));
    // Chart-level DOS = earliest DOS across coded lines + DOS-group headers (so an RFI/Disregard chart with a
    // DOS-only group still records ci.dos). Kept for patient history & sorting; age computed against it.
    $dos  = $chartDos;
    $age  = chart_age($dob, $dos);
    $gender = in_array(input('gender'), ['male','female','other','unknown'], true) ? (string) input('gender') : null;
    $pcpFlag = input('pcp_flag') === '1' ? 1 : 0;   // legacy PCP fields kept dormant (UI now uses Scribe?)
    $pcpName = $pcpFlag ? mb_substr(trim((string) input('pcp_provider_name')), 0, 150) : '';
    $pcpId = null;
    if ($pcpName !== '') foreach ($facProviders as $fp) if (strcasecmp((string) $fp['name'], $pcpName) === 0) { $pcpId = (int) $fp['id']; break; }
    $scribeFlag = input('scribe_flag') === '1' ? 1 : 0;   // Scribe? — when set, Rendering holds a scribe-type provider
    // A QA correction must NOT re-stamp submitted_at, re-attribute the coder, or re-roll audit sampling — it's the
    // auditor touching an already-submitted, already-sampled chart.
    $submitted = !$qaEdit && in_array($status, ['completed','rfi','disregard'], true);
    $saveCoderId = $qaEdit ? ($chart['coder_id'] ?: $uid) : $uid;

    // Bucket sampling: only completed charts are sampled (RFI/Disregard go to their own 100% queues).
    [$inSample, $samplePct] = $qaEdit
        ? [(int) ($chart['in_sample'] ?? 0), $chart['sample_pct']]
        : ($status === 'completed' ? coding_sample_decision($pdo, $uid) : [0, null]);

    // The TOP-priority flag is a "code this next" marker — it has done its job once the chart is COMPLETED, so
    // clear it automatically: no manual cleanup, and a stale flag can never keep skewing the next queue pick.
    // RFI / Disregard deliberately KEEP it — those charts are still in flight, and every send-back path
    // (coding-rto / coding-audit / coding-validate) re-sets is_priority=1 so reworks jump the queue.
    $clearPrio = ($status === 'completed') ? ', is_priority=0, priority_at=NULL' : '';

    $pdo->beginTransaction();
    $pdo->prepare("UPDATE coding_inventory SET
            patient_mrn=?, patient_name=?, patient_dob=?, record_sign_date=?,
            coded_provider_id=?, gender=?, dos=?, age_at_dos=?, pcp_flag=?,
            pcp_provider_id=?, pcp_provider_name=?, scribe_flag=?, rendering_provider_id=?, billing_provider_id=?,
            status=?, secondary_reason=?, comment=?, coder_id=?,
            in_sample=?, sample_pct=?,
            submitted_at=" . ($submitted ? 'NOW()' : 'submitted_at') . ", updated_at=NOW()$clearPrio
        WHERE id=?")->execute([
        trim((string) input('patient_mrn')) ?: null,
        mb_substr(trim((string) input('patient_name')), 0, 200),
        $dob, coding_parse_date(input('record_sign_date')),
        $codedProv, $gender, $dos, $age, $pcpFlag,
        $pcpId, ($pcpName !== '' ? $pcpName : null), $scribeFlag,
        ((int) input('rendering_provider_id')) ?: null,
        (((int) input('billing_provider_id')) ?: ($scribeFlag ? 0 : (int) input('rendering_provider_id'))) ?: null,   // billing default = rendering, but NEVER the scribe
        $status, ($secondary ?: null), ($comment ?: null), $saveCoderId,
        $inSample, $samplePct,
        $id,
    ]);
    coding_save_chart_lines($pdo, $id, $cpts);
    if ($qaEdit) {
        // The auditor DIRECTLY corrected the codes, so the prior per-code audit findings are SUPERSEDED (not deleted):
        // billing's coding_effective_lines_bulk skips superseded rows so it won't re-apply on top of the edited lines
        // and double-correct, while the quality report's defect-category panel and the coder's agreed rebuttal record
        // still see the original flagged codes (audit trail kept). Stored audit_result/defects/opportunities are left
        // untouched, so quality scoring is unaffected and the coder's agreed error stands.
        $pdo->prepare("UPDATE coding_audit_codes SET superseded_at=NOW() WHERE inventory_id=? AND superseded_at IS NULL")->execute([$id]);
        coding_log_event($pdo, $id, 'qa_correct', $uid, 'Codes corrected before release (auditor/supervisor)', null, $status);
    } else {
        coding_log_event($pdo, $id, 'coder_submit', $uid, $status === 'completed' ? ($inSample ? "sampled ({$samplePct}%)" : "not sampled ({$samplePct}%)") : $secondary, null, $status);
    }
    $pdo->commit();

    audit_log($qaEdit ? 'coding.chart.qa_correct' : 'coding.chart.submit', 'coding_inventory', $id, null, ['status'=>$status,'cpts'=>count($cpts),'in_sample'=>$inSample]);
    if ($qaEdit) {
        // A during-audit correction returns to the Audit page (safe /admin/ back-url only); a pre-release fix goes to Deliverables.
        $back = (string) ($_POST['back'] ?? '');
        if ($auditEditReq) {
            flash('success', 'Chart corrected — continue the audit.');
            redirect(($back !== '' && strpos($back, '/admin/') === 0) ? $back : ('/admin/coding-audit.php?id=' . $id));
        }
        flash('success', 'Chart corrected — it is ready to release to billing.');
        redirect('/admin/coding-deliverables.php');
    }
    flash('success', 'Chart saved as ' . str_replace('_', ' ', $status) . '.');
    if ($dupWarn) flash('warning', $dupWarn);
    // The coder chose what to do next: leave the queue, add ANOTHER manual chart (same c»p»f»provider), or get the next uploaded one.
    $after = (string) input('after');
    if ($after === 'exit') redirect('/admin/coding-inventory.php');
    if ($after === 'another' && ($chart['source'] ?? '') === 'manual') {
        $newId = coding_create_manual_chart($pdo, $uid, (int) $chart['client_id'], (int) $chart['process_id'], (int) $chart['facility_id'], (int) $chart['provider_id']);
        redirect('/admin/coding-chart.php?id=' . $newId);
    }
    redirect('/admin/coding-chart.php?next=1');
}

/* Existing lines for resume + patient history for the panel. */
$lines = coding_chart_lines($pdo, $id);
$history = coding_patient_history($pdo, (int) $chart['facility_id'], $chart['patient_mrn'], $chart['patient_name'], $id, 5);

/* Comment thread for THIS chart, read-only, in CHRONOLOGICAL order so it reads as the story:
 *   Coder (what they noted) → Validator (RFI/Disregard tagging note) → Validator note / Client response (send-back).
 * Sourced from the durable event log; the coder's own comment box stays separate & editable. Status-only
 * "sent back to coder" markers are dropped so only real comments show. */
$cmtHistory = $pdo->prepare("SELECT e.event_type, e.note, e.created_at, u.name actor
    FROM coding_chart_events e LEFT JOIN users u ON u.id=e.actor_id
    WHERE e.inventory_id=? AND e.event_type IN ('validate_tag','validate_return','client_response','rebuttal_resolve')
      AND COALESCE(e.note,'')<>''
      AND NOT (e.event_type='validate_return' AND e.note LIKE 'sent back to coder%')
    ORDER BY e.created_at ASC, e.id ASC LIMIT 50");
$cmtHistory->execute([$id]);
$cmtHistory = $cmtHistory->fetchAll();
// The coder's own comment is the origin of the thread — show it first.
if (trim((string)($chart['comment'] ?? '')) !== '') {
    array_unshift($cmtHistory, ['event_type'=>'coder_comment', 'note'=>$chart['comment'],
        'created_at'=>($chart['submitted_at'] ?? $chart['updated_at'] ?? null), 'actor'=>($chart['coder_name'] ?? '')]);
}
$cmtMeta = [
    'coder_comment'    => ['Coder', 'blue'],
    'validate_tag'     => ['Validator — RFI / Disregard', 'orange'],
    'validate_return'  => ['Validator note', 'indigo'],
    'client_response'  => ['Client response', 'azure'],
    'rebuttal_resolve' => ['Rebuttal', 'purple'],
];

$pageTitle = 'Code chart';
require __DIR__ . '/../includes/header.php';
?>
<link rel="stylesheet" href="<?= e(url('/admin/coding-ui.css?v=' . @filemtime(__DIR__ . '/coding-ui.css'))) ?>">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/css/tom-select.bootstrap5.min.css">
<?php
$stMap = ['pending'=>'secondary','in_progress'=>'info','hold'=>'warning','completed'=>'success','rfi'=>'orange','disregard'=>'danger'];
$codedProvName = $chart['uploaded_provider'] ?? '';
foreach ($facProviders as $fp) if ((int)$fp['id'] === $codedProvider) $codedProvName = $fp['name'];
$pcpName = (string) ($chart['pcp_provider_name'] ?? '');
if ($pcpName === '' && (int) $chart['pcp_provider_id']) foreach ($facProviders as $fp) if ((int)$fp['id'] === (int)$chart['pcp_provider_id']) $pcpName = $fp['name'];
?>
<div class="cw">

<?php if (!$editable): ?>
<div class="container-xl"><div class="alert alert-warning mt-2"><i class="ti ti-lock me-1"></i>This chart is <strong><?= e($chart['status']) ?></strong> and is read-only.</div></div>
<?php endif; ?>

<?php
/* Upfront duplicate-encounter alert (uses the chart's saved DOS; the authoritative guard re-runs on submit). */
$dupNow = ($chart['status'] !== 'completed' && !empty($chart['dos']))
    ? coding_duplicate_encounters($pdo, $id, (int) $chart['facility_id'], $chart['dos'],
        (string) ($chart['patient_mrn'] ?? ''), (string) ($chart['patient_name'] ?? ''), $chart['patient_dob'] ?? null, (int) $chart['provider_id'])
    : ['same' => [], 'diff' => []];
if ($dupNow['same'] || $dupNow['diff']): $d = $dupNow['same'][0] ?? $dupNow['diff'][0]; $hard = (bool) $dupNow['same']; ?>
<div class="container-xl"><div class="alert alert-<?= $hard ? 'danger' : 'warning' ?> mt-2 d-flex align-items-start">
    <i class="ti ti-alert-triangle me-2 mt-1"></i>
    <div><?php if ($hard): ?><strong>Possible duplicate encounter.</strong> This patient was already coded (completed) at this facility on <strong><?= e(fmt_date($chart['dos'])) ?></strong> for the <strong>same provider</strong><?= $d['provider'] ? ' (' . e($d['provider']) . ')' : '' ?> — chart #<?= (int) $d['id'] ?>. If this is a re-upload after a provider edit, <strong>tag it RFI</strong> instead of completing (completing will be blocked).
        <?php else: ?><strong>Same-day, different provider.</strong> Another provider<?= $d['provider'] ? ' (' . e($d['provider']) . ')' : '' ?> also has this patient at this facility on <strong><?= e(fmt_date($chart['dos'])) ?></strong> (chart #<?= (int) $d['id'] ?>). Coding this one is allowed — it will go to audit.<?php endif; ?></div>
</div></div>
<?php endif; ?>

<?php if ($codingState): /* out-of-form target so the Facility select (inside #chartForm) can post a facility change without triggering Save */ ?>
<form method="POST" action="<?= e(url('/admin/coding-chart.php?id='.$id)) ?>" id="facChangeForm" class="d-none"><?= csrf_field() ?><input type="hidden" name="action" value="change_facility"></form>
<?php endif; ?>

<form method="POST" action="<?= e(url('/admin/coding-chart.php?id='.$id)) ?>" id="chartForm" <?= $editable?'':'onsubmit="return false"' ?>>
<?= csrf_field() ?><input type="hidden" name="action" value="save"><input type="hidden" name="id" value="<?= $id ?>">
<?php if ($auditEditReq): ?><input type="hidden" name="audit_edit" value="1"><input type="hidden" name="back" value="<?= e((string)($_GET['back'] ?? $_POST['back'] ?? '')) ?>"><?php endif; ?>
<div class="page-body"><div class="container-xl">

    <!-- BILLING SHEET strip — horizontal, over the chart: Client » Process » Facility (this chart's) » Provider/scribe
         » Date of service (calendar). The matching document(s) for that provider + date appear horizontally; click View. -->
    <div class="card mb-3">
        <div class="card-header py-2"><h3 class="card-title mb-0"><i class="ti ti-file-invoice me-1 text-primary"></i>Billing sheet</h3>
            <span class="text-secondary small ms-2">Pick the provider/scribe and the date of service — the uploaded document(s) appear on the right; click View.</span></div>
        <div class="card-body py-2">
            <div class="row g-2 align-items-end">
                <div class="col-md-2"><label class="form-label mb-1 small">Client</label><select class="form-select form-select-sm" disabled><option><?= e($chart['cn']) ?></option></select></div>
                <div class="col-md-2"><label class="form-label mb-1 small">Process</label><select class="form-select form-select-sm" disabled><option><?= e($chart['pn']) ?></option></select></div>
                <div class="col-md-2"><label class="form-label mb-1 small">Facility<?php if ($codingState && count($facOptions) > 1): ?> <i class="ti ti-pencil text-primary" title="You can change this chart’s facility"></i><?php endif; ?></label>
                    <?php if ($codingState && count($facOptions) > 1): ?>
                    <select name="facility_id" form="facChangeForm" class="form-select form-select-sm" title="Change this chart’s facility — you will then re-select the provider"
                        onchange="if(this.value!=='<?= (int)$chart['facility_id'] ?>'){ if(confirm('Change this chart\'s facility to the selected one? You will re-select the provider for the new facility.')){ this.form.submit(); } else { this.value='<?= (int)$chart['facility_id'] ?>'; } }">
                        <?php foreach ($facOptions as $fo): ?><option value="<?= (int)$fo['id'] ?>" <?= (int)$fo['id']===(int)$chart['facility_id']?'selected':'' ?>><?= e($fo['name']) ?></option><?php endforeach; ?>
                    </select>
                    <?php else: ?>
                    <select class="form-select form-select-sm" disabled><option><?= e($chart['fn']) ?></option></select>
                    <?php endif; ?>
                </div>
                <div class="col-md-3"><label class="form-label mb-1 small">Provider / scribe</label>
                    <select id="bs_prov" class="form-select form-select-sm"><option value="">— provider / scribe —</option>
                        <?php foreach ($bsProvOpts as $o): ?><option value="<?= (int)$o['id'] ?>" <?= (int)$o['id']===$bsPreProv?'selected':'' ?>><?= e($o['name']) ?></option><?php endforeach; ?>
                    </select></div>
                <div class="col-md-3"><label class="form-label mb-1 small">Date of service</label><input type="date" id="bs_date" class="form-control form-control-sm" value="<?= e($bsDos) ?>"></div>
            </div>
            <div id="bs_docs" class="mt-2 d-flex flex-wrap align-items-center gap-2"></div>
        </div>
    </div>
    <script>
    (function(){
        var SHEETS = <?= json_encode(array_map(fn($s)=>['id'=>(int)$s['id'],'prid'=>(int)$s['provider_id'],'sid'=>(int)$s['scribe_id'],'d'=>$s['d'],'cnt'=>(int)$s['patient_count'],'name'=>($s['prov_name']?:'(provider)'),'scr'=>(int)$s['is_scribe']], $bsSheets), JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_QUOT|JSON_HEX_APOS) ?>;
        var elP=document.getElementById('bs_prov'), elD=document.getElementById('bs_date'), docs=document.getElementById('bs_docs'), base='<?= e(url('/admin/coding-bs-view.php')) ?>';
        function esc(t){ var d=document.createElement('div'); d.textContent=t; return d.innerHTML; }
        function render(){ var pv=elP.value, d=elD.value;
            if(!pv){ docs.innerHTML='<span class="text-secondary small">Pick a provider/scribe to see its uploaded document(s).</span>'; return; }
            var m=SHEETS.filter(function(s){ return (String(s.prid)===pv || String(s.sid)===pv) && (!d || s.d===d); });
            if(!m.length){ docs.innerHTML='<span class="text-secondary small"><i class="ti ti-info-circle me-1"></i>No document for this provider/scribe'+(d?(' on '+esc(d)):'')+'.</span>'; return; }
            docs.innerHTML = '<span class="small text-secondary me-1">'+m.length+' document(s):</span>' + m.map(function(s){
                var badge = s.scr ? '<i class="ti ti-pencil text-purple me-1"></i>'+esc(s.name)+' (scribe)' : esc(s.name);
                return '<a href="'+base+'?id='+s.id+'" target="_blank" class="btn btn-sm btn-outline-primary d-inline-flex align-items-center gap-1" title="View document">'
                    +'<i class="ti ti-file-text"></i><span>'+badge+' · '+esc(s.d)+' · '+s.cnt+' pt</span><i class="ti ti-eye"></i></a>'; }).join('');
        }
        elP.addEventListener('change', render); elD.addEventListener('change', render); render();
    })();
    </script>

    <?php if (!empty($qaEdit)): ?>
    <div class="alert alert-warning d-flex align-items-center"><i class="ti ti-edit me-2"></i>
        <div><strong>Pre-release edit.</strong> This chart is audited but not yet released to billing — fix the codes here and <strong>Save</strong> and it returns to <em>Deliverables</em> with the corrected codes, ready to release. The coder's recorded quality is unchanged.</div>
    </div>
    <?php endif; ?>

    <div class="row">

    <!-- LEFT: demographics + coding -->
    <div class="col-lg-8">
        <div class="card mb-2">
            <div class="card-header cw-pt-head">
                <div class="cw-pt-id">
                    <div class="cw-pt-name"><i class="ti ti-user me-1 text-primary"></i><?= e($chart['patient_name'] ?: 'Chart #' . $id) ?></div>
                    <div class="cw-pt-meta"><?= e($chart['cn']) ?> · <?= e($chart['pn']) ?> · <i class="ti ti-building-hospital"></i> <?= e($chart['fn']) ?> · uploaded <?= e(fmt_dt($chart['uploaded_at'])) ?></div>
                </div>
                <div class="cw-pt-actions ms-auto">
                    <?php if ((int)$chart['is_priority']): ?><span class="cw-pill bg-red text-white"><i class="ti ti-arrow-up"></i> TOP</span><?php endif; ?>
                    <span class="cw-pill bg-<?= $stMap[$chart['status']] ?? 'secondary' ?>-lt"><?= e(str_replace('_',' ',$chart['status'])) ?></span>
                    <label class="form-label d-inline mb-0 small text-secondary">Provider:</label>
                    <select name="coded_provider_id" id="codedProvider" class="form-select form-select-sm d-inline-block w-auto" <?= $editable?'':'disabled' ?>>
                        <?php foreach ($facProviders as $fp): ?><option value="<?= (int)$fp['id'] ?>" <?= $codedProvider===(int)$fp['id']?'selected':'' ?>><?= e($fp['name']) ?></option><?php endforeach; ?>
                    </select>
                    <a href="<?= e(url('/admin/coding-inventory.php')) ?>" class="btn btn-sm btn-outline-secondary"><i class="ti ti-arrow-left me-1"></i>Queue</a>
                </div>
            </div>
            <div class="card-body cw-tight">
                <!-- Row 1: Patient name · DOB · Record sign date · PCP? · Find -->
                <div class="row g-2 align-items-end">
                    <div class="col-md"><label class="form-label req">Patient name</label><input name="patient_name" id="f_name" class="form-control form-control-sm req-demo" value="<?= e($chart['patient_name']) ?>" required <?= (($chart['source'] ?? '') === 'manual') ? 'autofocus' : '' ?>></div>
                    <div class="col-md"><label class="form-label req">DOB</label><input type="date" name="patient_dob" id="f_dob" class="form-control form-control-sm req-demo" value="<?= e($chart['patient_dob']) ?>"></div>
                    <div class="col-md"><label class="form-label req">Record sign date</label>
                        <input type="date" name="record_sign_date" id="f_rsd" class="form-control form-control-sm req-demo" value="<?= e($chart['record_sign_date']) ?>"></div>
                    <div class="col-md"><label class="form-label">Scribe?</label>
                        <select name="scribe_flag" id="f_scribe" class="form-select form-select-sm" title="If Yes, type the scribe (by name or NPI) in the Rendering provider field."><option value="0" <?= empty($chart['scribe_flag'])?'selected':'' ?>>No</option><option value="1" <?= !empty($chart['scribe_flag'])?'selected':'' ?>>Yes</option></select></div>
                    <div class="col-md-auto"><label class="form-label">&nbsp;</label><button type="button" class="btn btn-sm btn-outline-primary w-100" id="btnFind" title="Find an existing patient & pull demographics + history"><i class="ti ti-search me-1"></i>Find patient</button></div>
                </div>
                <div id="lookupNote" class="small mt-1"></div>
                <!-- Row 2: Rendering provider · Billing provider · Patient MRN/PRN · Gender (· PCP provider when PCP=Yes) -->
                <div class="row g-2 mt-1 align-items-end">
                    <div class="col-md"><label class="form-label req">Rendering provider</label>
                        <select name="rendering_provider_id" id="f_render" class="form-select form-select-sm req-demo"><option value="">—</option>
                            <?php foreach ($facProviders as $fp): ?><option value="<?= (int)$fp['id'] ?>" <?= (int)($chart['rendering_provider_id']?:$codedProvider)===(int)$fp['id']?'selected':'' ?>><?= e($fp['name']) ?></option><?php endforeach; ?></select></div>
                    <div class="col-md"><label class="form-label req">Billing provider</label>
                        <select name="billing_provider_id" id="f_bill" class="form-select form-select-sm req-demo"><option value="">—</option>
                            <?php $billSel = (int)($chart['billing_provider_id'] ?: ($chart['rendering_provider_id'] ?: $codedProvider));   // default billing = rendering (most charts share it)
                            foreach ($facProviders as $fp): ?><option value="<?= (int)$fp['id'] ?>" <?= $billSel===(int)$fp['id']?'selected':'' ?>><?= e($fp['name']) ?></option><?php endforeach; ?></select></div>
                    <div class="col-md"><label class="form-label req">Patient MRN/PRN</label><input name="patient_mrn" id="f_mrn" class="form-control form-control-sm req-demo" value="<?= e($chart['patient_mrn']) ?>"></div>
                    <div class="col-md"><label class="form-label req">Gender</label>
                        <select name="gender" id="f_gender" class="form-select form-select-sm req-demo" <?= (($chart['source'] ?? '') === 'manual') ? '' : 'autofocus' ?>>
                            <option value="">—</option>
                            <?php foreach (['male'=>'Male','female'=>'Female','other'=>'Other','unknown'=>'Unknown'] as $k=>$v): ?>
                            <option value="<?= e($k) ?>" <?= $chart['gender']===$k?'selected':'' ?>><?= e($v) ?></option><?php endforeach; ?>
                        </select></div>
                </div>
            </div>
        </div>

        <div class="card mb-3">
            <div class="card-header"><h3 class="card-title"><i class="ti ti-list-numbers me-1"></i>Codes by date of service</h3>
                <div class="card-actions"><button type="button" class="btn btn-sm btn-primary" id="addDos"><i class="ti ti-calendar-plus me-1"></i>Add date of service</button></div>
            </div>
            <div class="card-body">
                <div id="dosGroups"></div>
                <div id="codeNote" class="code-note warn" style="display:none"></div>

                <!-- SUBMIT — part of the Codes container (bottom): compact 2-row layout -->
                <div class="cw-submit">
                    <div class="row g-2 align-items-end">
                        <div class="col-md-3"><label class="form-label">Status</label>
                            <select name="status" id="f_status" class="form-select form-select-sm">
                                <option value="completed">Completed (send to audit)</option>
                                <option value="hold" <?= $chart['status']==='hold'?'selected':'' ?>>Hold (resume later)</option>
                                <option value="rfi">RFI (info from client)</option>
                                <option value="disregard">Disregard (not codable)</option>
                            </select></div>
                        <div class="col-md-3" id="secWrap" style="display:none"><label class="form-label required">Secondary reason</label>
                            <select name="secondary_reason" id="f_secondary" class="form-select form-select-sm"><option value="">—</option></select></div>
                        <div class="col-md"><label class="form-label" id="commentLabel">Comment</label>
                            <textarea name="comment" id="f_comment" class="form-control form-control-sm" rows="2" placeholder="Notes / reason" style="field-sizing:content;min-height:2.4em;max-height:400px;overflow:auto"><?= e($chart['comment']) ?></textarea></div>
                    </div>
                    <?php if ($editable): ?>
                    <div class="d-flex align-items-center gap-2 mt-2 flex-wrap">
                        <button type="submit" name="draft" value="1" class="btn btn-sm btn-outline-primary" id="saveCodes"><i class="ti ti-device-floppy me-1"></i>Save codes</button>
                        <span id="saveNote" class="small text-secondary"></span>
                        <div class="ms-auto d-flex gap-2">
                            <button class="btn btn-sm btn-outline-primary" name="after" value="exit"><i class="ti ti-logout me-1"></i>Submit &amp; Exit to queue</button>
                            <?php if (($chart['source'] ?? '') === 'manual'): ?>
                            <button class="btn btn-sm btn-primary" name="after" value="another"><i class="ti ti-file-plus me-1"></i>Submit &amp; add another chart</button>
                            <?php else: ?>
                            <button class="btn btn-sm btn-primary" name="after" value="next"><i class="ti ti-device-floppy me-1"></i>Submit &amp; Get Next Chart</button>
                            <?php endif; ?>
                        </div>
                    </div>
                    <?php endif; ?>
                </div>
            </div>
        </div>
    </div>

    <!-- RIGHT: comment history (validator / client) + past coded DOS -->
    <div class="col-lg-4"><div class="cw-rail">
        <?php if ($cmtHistory): ?>
        <div class="card mb-3">
            <div class="card-header py-2"><h3 class="card-title mb-0"><i class="ti ti-messages me-1"></i>Comment history</h3>
                <div class="card-actions"><span class="badge bg-blue-lt"><?= count($cmtHistory) ?></span></div></div>
            <div class="card-body p-2" style="max-height:320px;overflow:auto">
                <?php foreach ($cmtHistory as $h): [$lbl,$col] = $cmtMeta[$h['event_type']] ?? ['Note','secondary']; ?>
                    <div class="mb-2 pb-2 border-bottom">
                        <div class="d-flex align-items-center gap-2" style="font-size:.72rem">
                            <span class="badge bg-<?= $col ?>-lt"><?= e($lbl) ?></span>
                            <span class="text-secondary"><?= e($h['actor'] ?? '') ?></span>
                            <span class="text-secondary ms-auto"><?= e(fmt_dt($h['created_at'])) ?></span>
                        </div>
                        <div class="small mt-1"><?= nl2br(e($h['note'])) ?></div>
                    </div>
                <?php endforeach; ?>
            </div>
        </div>
        <?php endif; ?>
        </div><!-- /.cw-rail -->
    </div><!-- /col-lg-4 -->
    </div><!-- /.row -->

    <!-- Past coded DOS — full width below the chart; ONE ultra-compact table. Demographic columns (same fields as
         the live demographic container) are printed once per prior chart on its first line and left blank on its
         remaining code lines; the chart's codes follow per date of service. -->
    <div class="card mt-2">
        <div class="card-header py-2"><h3 class="card-title mb-0"><i class="ti ti-history me-1"></i>Past coded DOS</h3>
            <?php if ($history): ?><div class="card-actions"><span class="badge bg-blue-lt"><?= count($history) ?> prior chart(s)</span></div><?php endif; ?></div>
        <div class="card-body p-0"><div id="histBox">
            <?php
            $stColor = ['completed'=>'success','rfi'=>'orange','disregard'=>'danger','in_progress'=>'info','hold'=>'warning','pending'=>'secondary'];
            $genderLabels = ['male'=>'Male','female'=>'Female','other'=>'Other','unknown'=>'Unknown'];
            if (!$history): ?><div class="text-secondary text-center py-3 small">No prior coded DOS for this patient.</div>
            <?php else: ?>
            <div class="table-responsive"><table class="table table-sm table-vcenter card-table cw-hist-flat mb-0">
                <thead><tr>
                    <th>Patient</th><th>MRN/PRN</th><th>DOB</th><th>Gender</th><th>Rec sign</th><th>Scribe?</th>
                    <th>Rendering</th><th>Billing</th><th>Coded prov</th><th>Status</th><th>Audit</th>
                    <th>DOS</th><th>CPT</th><th class="text-center">U</th><th>Mods</th><th>ICD code(s)</th>
                </tr></thead>
                <tbody>
                <?php foreach ($history as $h):
                    $stBadge  = '<span class="badge bg-' . ($stColor[$h['status']] ?? 'secondary') . '-lt">' . e(str_replace('_',' ',$h['status'])) . '</span>';
                    $audBadge = '';
                    if (!empty($h['audit_status'])) {
                        $ac = $h['audit_status']==='ready_to_deliver' ? 'green' : ($h['audit_status']==='rfi' ? 'orange' : 'danger');
                        $audBadge = '<span class="badge bg-' . $ac . '-lt" title="Audit outcome"><i class="ti ti-checklist"></i> ' . e(str_replace('_',' ',$h['audit_status'])) . ($h['accuracy']!==null ? ' · '.$h['accuracy'].'%' : '') . '</span>';
                    }
                    $prov   = e($h['provider'] ?? '—') . (!empty($h['auditor']) ? ' <span class="text-secondary">· aud: '.e($h['auditor']).'</span>' : '');
                    $dobTxt = $h['patient_dob'] ? e(fmt_date($h['patient_dob'])) . (($h['age_at_dos'] !== null && $h['age_at_dos'] !== '') ? ' <span class="text-secondary">('.(int)$h['age_at_dos'].'y)</span>' : '') : '—';
                    $scribe = !empty($h['scribe_flag']) ? ('Yes · ' . e($h['rendering_provider'] ?: '—')) : 'No';
                    // flatten the chart's lines into rows, grouped by date of service (most-recent DOS first)
                    $byDos = []; foreach (($h['lines'] ?: []) as $ln) { $byDos[$ln['dos'] ?: ''][] = $ln; }
                    krsort($byDos);
                    $rows = []; foreach ($byDos as $dosKey => $lns) { $firstInDos = true; foreach ($lns as $ln) { $rows[] = [$dosKey, $ln, $firstInDos]; $firstInDos = false; } }
                    if (!$rows) $rows[] = [null, null, true];   // a chart with no codes still shows its demographics
                    $chartFirst = true;
                    foreach ($rows as [$dosKey, $ln, $firstInDos]):
                ?>
                    <tr class="<?= $chartFirst ? 'cw-hist-grp' : '' ?>">
                        <td class="cw-demo" title="<?= e($h['patient_name'] ?? '') ?>"><?= $chartFirst ? e($h['patient_name'] ?: '—') : '' ?></td>
                        <td class="cw-demo"><?= $chartFirst ? e($h['patient_mrn'] ?: '—') : '' ?></td>
                        <td class="cw-demo text-nowrap"><?= $chartFirst ? $dobTxt : '' ?></td>
                        <td class="cw-demo"><?= $chartFirst ? e($genderLabels[$h['gender']] ?? '—') : '' ?></td>
                        <td class="cw-demo text-nowrap"><?= $chartFirst ? ($h['record_sign_date'] ? e(fmt_date($h['record_sign_date'])) : '—') : '' ?></td>
                        <td class="cw-demo"><?= $chartFirst ? $scribe : '' ?></td>
                        <td class="cw-demo" title="<?= e($h['rendering_provider'] ?? '') ?>"><?= $chartFirst ? e($h['rendering_provider'] ?: '—') : '' ?></td>
                        <td class="cw-demo" title="<?= e($h['billing_provider'] ?? '') ?>"><?= $chartFirst ? e($h['billing_provider'] ?: '—') : '' ?></td>
                        <td class="cw-demo"><?= $chartFirst ? $prov : '' ?></td>
                        <td><?= $chartFirst ? $stBadge : '' ?></td>
                        <td><?= $chartFirst ? ($audBadge ?: '—') : '' ?></td>
                        <td class="text-nowrap"><?= $ln === null ? '<span class="text-secondary">— no codes —</span>' : ($firstInDos ? ($dosKey ? e(fmt_date($dosKey)) : '<span class="text-secondary">—</span>') : '') ?></td>
                        <td class="cw-cpt"><?= $ln ? e($ln['cpt']) : '' ?></td>
                        <td class="text-center"><?= $ln ? (int)($ln['units'] ?? 1) : '' ?></td>
                        <td><?= $ln ? (e(implode(', ', $ln['mods'])) ?: '—') : '' ?></td>
                        <td><?= $ln ? (e(implode(', ', $ln['icds'])) ?: '—') : '' ?></td>
                    </tr>
                <?php $chartFirst = false; endforeach; endforeach; ?>
                </tbody>
            </table></div>
            <?php endif; ?>
        </div></div>
    </div>
</div></div>
</form>

</div><!-- /.cw -->

<script>
window.CODING = {
    api: <?= json_encode(url('/admin/coding-api.php')) ?>,
    csrf: <?= json_encode(csrf_token()) ?>,
    facility: <?= (int)$chart['facility_id'] ?>,
    provider: <?= $codedProvider ?>,
    excludeId: <?= $id ?>,
    modifiers: <?= json_encode(coding_modifiers($pdo)) ?>,
    rfiReasons: <?= json_encode(array_values(coding_secondary_reasons($pdo,'rfi'))) ?>,
    disReasons: <?= json_encode(array_values(coding_secondary_reasons($pdo,'disregard'))) ?>,
    existing: <?= json_encode(array_map(fn($l)=>['code'=>$l['cpt_code'],'units'=>(int)($l['units'] ?? 1),'dos'=>$l['dos'],'icds'=>$l['icds'],'mods'=>$l['mods']], $lines)) ?>,
    editable: <?= $editable ? 'true' : 'false' ?>
};
</script>
<script src="<?= e(url('/admin/coding-chart.js?v=' . @filemtime(__DIR__ . '/coding-chart.js'))) ?>"></script>
<script>
/* keep the EHR banner in sync with the form fields (DOS/age now live per-DOS in the Codes section) */
(function(){
  var g=function(id){return document.getElementById(id);};
  var mrn=g('f_mrn'),prov=g('codedProvider');
  function sync(){
    if(g('bMrn')) g('bMrn').textContent=(mrn&&mrn.value.trim())||'—';
    if(g('bProv')&&prov&&prov.selectedIndex>=0) g('bProv').textContent=prov.options[prov.selectedIndex].text;
  }
  [mrn,prov].forEach(function(el){ if(el){ ['input','change'].forEach(function(ev){ el.addEventListener(ev,function(){ setTimeout(sync,30); }); }); } });

  /* (Copy Record-sign-date → DOS now lives as a per-DOS button in the Codes section; see coding-chart.js .rsd-here) */

  /* Mirror the coding Date of service into the Billing-sheet "Date of service" (top-right) so the matching
   * uploaded document surfaces automatically. The first DOS group is the chart's primary DOS. */
  (function(){
    var dosWrap=document.getElementById('dosGroups'), bsDate=document.getElementById('bs_date');
    if(!dosWrap || !bsDate) return;
    function syncBsDate(){
      // Mirror the chart's PRIMARY date of service = the EARLIEST DOS across all groups. This matches the server's
      // stored chart.dos = min(line DOS), so on load it equals the pre-selected #bs_date and never clobbers it
      // (DOM group order is CPT-entry order, not date order, so picking the first group would pick the wrong date).
      var dates=[].map.call(dosWrap.querySelectorAll('.dos-date'), function(d){ return d.value; }).filter(Boolean).sort();
      var v=dates[0]||'';
      if(v && bsDate.value!==v){ bsDate.value=v; bsDate.dispatchEvent(new Event('change',{bubbles:true})); }   // change → re-filter the billing-sheet document list
    }
    function onDosEvt(ev){ if(ev.target && ev.target.classList && ev.target.classList.contains('dos-date')) syncBsDate(); }
    dosWrap.addEventListener('change', onDosEvt);
    dosWrap.addEventListener('input',  onDosEvt);
    syncBsDate();   // reflect an already-coded DOS on load
  })();
})();
</script>
<!-- Searchable Rendering / Billing provider pickers: type to filter, Enter/Tab to select. TomSelect keeps the
     underlying <select> value in sync, so the existing form-save logic is unaffected. -->
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/js/tom-select.complete.min.js"></script>
<script>
(function(){
  if (typeof TomSelect === 'undefined') return;   // CDN blocked → native select still works
  ['f_render','f_bill'].forEach(function(id){
    var el = document.getElementById(id);
    if (el && !el.tomselect) new TomSelect(el, {
      maxOptions: 2000, persist: false, allowEmptyOption: false,   // hide the blank "—" from the search list
      placeholder: 'Type to search a provider…',
      sortField: { field: 'text', direction: 'asc' }
    });
  });

  // After the coder picks a Rendering provider, advance the cursor to Billing provider (don't sit in the same box).
  (function(){
    var rTs=(document.getElementById('f_render')||{}).tomselect, bTs=(document.getElementById('f_bill')||{}).tomselect;
    if(!rTs || !bTs) return;
    var ready=false; setTimeout(function(){ ready=true; }, 500);   // ignore the pre-selected value added during init
    rTs.on('item_add', function(){
      if(!ready) return;                                                  // only after load → only real user picks
      setTimeout(function(){
        rTs.blur();
        var oof=bTs.settings.openOnFocus; bTs.settings.openOnFocus=false; // focus Billing WITHOUT popping its dropdown
        bTs.focus();
        setTimeout(function(){ bTs.settings.openOnFocus=oof; }, 0);       // restore so a normal click still opens it
      }, 0);
    });
  })();

  // ---- Scribe? mode: when Yes, the Rendering picker searches SCRIBES (scribe candidates for THIS facility — any
  // active provider not billing here) instead of facility billing providers. The chosen scribe is saved as
  // rendering_provider_id; billing_provider_id stays the real billing provider. ----
  var scr=document.getElementById('f_scribe'), rend=document.getElementById('f_render'), ts=rend&&rend.tomselect;
  if(!scr || !ts) return;
  var SCRIBES = <?= json_encode(array_map(fn($s)=>['id'=>(int)$s['id'],'name'=>$s['name'].($s['npi']?(' · NPI '.$s['npi']):'')], $scribes), JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_QUOT|JSON_HEX_APOS) ?>;
  var SAVED   = <?= json_encode($savedScribe ? ['id'=>(int)$savedScribe['id'],'name'=>$savedScribe['name']] : null) ?>;
  var BILLING = [].map.call(rend.options, function(o){ return {value:o.value, text:o.textContent}; }).filter(function(o){ return o.value!==''; });
  function setOpts(list, keep){
    ts.clear(true); ts.clearOptions();                  // clear selection first so no stale (e.g. scribe) option lingers; no blank "—" entry
    list.forEach(function(o){ ts.addOption({value:String(o.value!=null?o.value:o.id), text:o.text||o.name}); });
    ts.refreshOptions(false);
    if(keep!=null) ts.setValue(String(keep), true);
  }
  function ph(t){ if(ts.control_input) ts.control_input.placeholder=t; }
  function apply(){
    if(scr.value==='1'){
      var list = SCRIBES.map(function(s){ return {value:s.id, text:s.name}; }), keep=null;
      if(SAVED && SAVED.id){ if(!SCRIBES.some(function(s){ return s.id===SAVED.id; })) list.unshift({value:SAVED.id, text:SAVED.name}); keep=SAVED.id; }
      setOpts(list, keep); ph('Type a scribe name / NPI…');
    } else {
      setOpts(BILLING, BILLING.some(function(o){ return String(o.value)===String(rend.value); }) ? rend.value : null); ph('Type to search a provider…');
    }
  }
  scr.addEventListener('change', apply);
  if(scr.value==='1') apply();   // existing scribe chart → enter scribe mode on load
})();
</script>
<script>
/* Allow PASTING dates into the native date inputs (DOB · Record sign date · DOS). Browsers reject pasted text that
   isn't the picker's exact format, so we intercept the paste, parse the common forms — m/d/Y (matches fmt_date),
   Y-m-d, m-d-Y, or anything Date can read like "Aug 3 1951" — and set the input's canonical yyyy-mm-dd value. */
(function(){
    function pad(n){ n = String(n); return n.length < 2 ? '0' + n : n; }
    function toISO(raw){
        var s = (raw || '').trim(); if (!s) return ''; var m;
        if ((m = s.match(/^(\d{4})[-\/.](\d{1,2})[-\/.](\d{1,2})$/))) return m[1] + '-' + pad(m[2]) + '-' + pad(m[3]);   // Y-m-d
        if ((m = s.match(/^(\d{1,2})[-\/.](\d{1,2})[-\/.](\d{4})$/))) return m[3] + '-' + pad(m[1]) + '-' + pad(m[2]);   // m/d/Y (US)
        var d = new Date(s);
        if (!isNaN(d.getTime())) return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());
        return '';
    }
    function attach(el){
        if (el.dataset.pasteReady) return; el.dataset.pasteReady = '1';
        el.addEventListener('paste', function(e){
            var cd = e.clipboardData || window.clipboardData; if (!cd) return;
            var iso = toISO(cd.getData('text'));
            if (iso){ e.preventDefault(); el.value = iso;
                el.dispatchEvent(new Event('input',  { bubbles:true }));
                el.dispatchEvent(new Event('change', { bubbles:true })); }
        });
    }
    document.querySelectorAll('input[type="date"]').forEach(attach);
    // Catch any date inputs added/re-rendered later (the chart form refreshes via coding-chart.js).
    document.addEventListener('focusin', function(e){ if (e.target.matches && e.target.matches('input[type="date"]')) attach(e.target); });
})();
</script>
<?php require __DIR__ . '/../includes/footer.php'; ?>
