<?php
/**
 * Client portal — OVERALL dashboard. Mirrors the internal AR manager overview (admin/workspace.php)
 * "same to same": KPI strip + Revenue/A-R trend + Collection-rate gauge + Balance-by-process bar + Aging
 * donut — but scoped to the CLIENT this user is assigned to, in the read-only client portal shell. A
 * per-dashboard drill-in table follows (the client has no sidebar to browse from).
 */
require_once __DIR__ . '/../includes/config.php';
$user = require_login('client');
$pdo  = db();
$clientId = (int) $user['client_id'];

$brand = $pdo->prepare('SELECT c.name AS client_name, cb.primary_color, cb.login_slug
    FROM clients c LEFT JOIN client_branding cb ON cb.client_id = c.id
    WHERE c.id = ? AND c.deleted_at IS NULL');
$brand->execute([$clientId]); $brand = $brand->fetch();
if (!$brand) { logout(); redirect('/login.php'); }
$cp_primary = $brand['primary_color'] ?: '#206bc4';
$cp_brand   = $brand['client_name'];
$cp_slug    = (string) ($brand['login_slug'] ?? '');
$cp_title   = 'Overall · ' . $cp_brand;
$cp_hide_back = true;   // this IS the client landing — no "back to dashboard" button

/* Visible COLLECTION dashboards = the user's HOME client's reports UNION any reports explicitly GRANTED to them
   via report_access (which may belong to OTHER clients — a client contact who oversees several clients). */
$allowed = accessible_report_ids((int) $user['id']);     // granted report ids (array; null only for sysadmin)
$grantClause = (is_array($allowed) && $allowed) ? (' OR r.id IN (' . implode(',', array_map('intval', $allowed)) . ')') : '';
$st = $pdo->prepare("SELECT r.id, r.name, p.name process_name, c.name client_name
    FROM reports r JOIN processes p ON p.id = r.process_id JOIN clients c ON c.id = p.client_id
    WHERE r.deleted_at IS NULL AND r.status='active' AND r.handler='collection_dashboard'
      AND (p.client_id = ?" . $grantClause . ")
    ORDER BY c.name, r.name");
$st->execute([$clientId]);
$reports   = $st->fetchAll();
$reportIds = array_map(fn($r) => (int) $r['id'], $reports);
$reportCount = count($reports);
$clientCt    = count(array_unique(array_map(fn($r) => (string) $r['client_name'], $reports)));
/* The banner is branded to the home client only when this login really covers ONE client; a multi-client
   overseer gets an aggregate label so the brand never implies the dashboard is just that one client. */
if ($clientCt > 1) {
    $cp_brand = 'All clients (' . $clientCt . ')';
    $cp_title = 'Overall · all clients';
}

/* ── Analytics over collection_raw, scoped to this client's collection reports (mirrors workspace.php). ── */
$months = []; $byClient = []; $aging = []; $byReport = [];
$tot = ['charge'=>0,'paid'=>0,'expected'=>0,'ins_balance'=>0,'pat_balance'=>0,'claims'=>0,'paid_n'=>0];
$hasData = false;
if ($reportIds) {
    $idsCsv   = implode(',', $reportIds);                 // ints from DB — safe to inline
    $scopeRaw = " AND report_id IN ($idsCsv)";            // collection_raw (no alias)
    $scopeCr  = " AND cr.report_id IN ($idsCsv)";         // collection_raw cr
    try {
        /* Monthly trend + grand totals */
        foreach ($pdo->query("
            SELECT dos_year, dos_month,
                   SUM(charge_amount) charge,
                   SUM(primary_ins_paid + secondary_ins_paid + tertiary_ins_paid + patient_payment_amount) paid,
                   SUM(expected_payment) expected,
                   SUM(CASE WHEN insurance_balance > 0 THEN insurance_balance ELSE 0 END) ins_balance,
                   SUM(CASE WHEN patient_balance > 0 THEN patient_balance ELSE 0 END) pat_balance,
                   COUNT(DISTINCT visit_id) claims,
                   COUNT(DISTINCT CASE WHEN primary_ins_paid > 0 THEN visit_id END) paid_n
              FROM collection_raw
             WHERE dos_year IS NOT NULL AND dos_month IS NOT NULL" . $scopeRaw . "
             GROUP BY dos_year, dos_month ORDER BY dos_year, dos_month")->fetchAll() as $r) {
            $mn = ['', 'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][(int)$r['dos_month']] ?? '';
            $months[] = [
                'label' => $mn . ' ' . substr((string)$r['dos_year'], -2),
                'charge'=>round((float)$r['charge'],2), 'paid'=>round((float)$r['paid'],2),
                'expected'=>round((float)$r['expected'],2),
                'ins_balance'=>round((float)$r['ins_balance'],2),
                'pat_balance'=>round((float)$r['pat_balance'],2),
                'claims'=>(int)$r['claims'], 'paid_n'=>(int)$r['paid_n'],
            ];
            $tot['charge']+= (float)$r['charge']; $tot['paid']+= (float)$r['paid'];
            $tot['expected']+= (float)$r['expected'];
            $tot['ins_balance']+= (float)$r['ins_balance']; $tot['pat_balance']+= (float)$r['pat_balance'];
            $tot['claims']+= (int)$r['claims']; $tot['paid_n']+= (int)$r['paid_n'];
        }
        /* Top 12 processes by open balance (ins + pat) */
        foreach ($pdo->query("
            SELECT CONCAT(COALESCE(c.name,'(unknown)'),' » ',COALESCE(p.name,'(process)')) client,
                   SUM(CASE WHEN cr.insurance_balance > 0 THEN cr.insurance_balance ELSE 0 END) ins_balance,
                   SUM(CASE WHEN cr.patient_balance > 0 THEN cr.patient_balance ELSE 0 END) pat_balance
              FROM collection_raw cr
              JOIN reports rep ON rep.id = cr.report_id AND rep.deleted_at IS NULL
              JOIN processes p ON p.id = rep.process_id
              JOIN clients   c ON c.id = p.client_id
             WHERE 1=1" . $scopeCr . "
             GROUP BY p.id ORDER BY (SUM(cr.insurance_balance) + SUM(cr.patient_balance)) DESC LIMIT 12")->fetchAll() as $r) {
            $byClient[] = ['client'=>$r['client'], 'ins_balance'=>round((float)$r['ins_balance'],2), 'pat_balance'=>round((float)$r['pat_balance'],2)];
        }
        /* Aging of open balance */
        $agMap = ['0-30'=>0,'31-60'=>0,'61-90'=>0,'91-120'=>0,'121+'=>0];
        foreach ($pdo->query("
            SELECT CASE WHEN DATEDIFF(CURDATE(),dos)<=30 THEN '0-30'
                        WHEN DATEDIFF(CURDATE(),dos)<=60 THEN '31-60'
                        WHEN DATEDIFF(CURDATE(),dos)<=90 THEN '61-90'
                        WHEN DATEDIFF(CURDATE(),dos)<=120 THEN '91-120' ELSE '121+' END bucket,
                   SUM(insurance_balance + patient_balance) bal
              FROM collection_raw
             WHERE (insurance_balance > 0 OR patient_balance > 0) AND dos IS NOT NULL" . $scopeRaw . "
             GROUP BY bucket")->fetchAll() as $r) { $agMap[$r['bucket']] = round((float)$r['bal'],2); }
        $aging = $agMap;
        /* Per-dashboard rollup for the drill-in table */
        $perMetric = "SUM(COALESCE(charge_amount,0)) charges,
                      SUM(COALESCE(primary_ins_paid,0)+COALESCE(secondary_ins_paid,0)+COALESCE(tertiary_ins_paid,0)+COALESCE(patient_payment_amount,0)) collected,
                      SUM(COALESCE(insurance_balance,0)+COALESCE(patient_balance,0)) outstanding";
        foreach ($pdo->query("SELECT report_id, $perMetric FROM collection_raw WHERE report_id IN ($idsCsv) GROUP BY report_id") as $r)
            $byReport[(int)$r['report_id']] = $r;
        foreach ($pdo->query("SELECT report_id, MAX(completed_at) lu FROM collection_uploads
                              WHERE report_id IN ($idsCsv) AND kind='collection' AND status='completed' GROUP BY report_id") as $r)
            if (isset($byReport[(int)$r['report_id']])) $byReport[(int)$r['report_id']]['lu'] = $r['lu'];
        $hasData = $tot['charge'] > 0 || $tot['claims'] > 0;
    } catch (Throwable $e) { /* collection schema not present */ }
}
$collPct = $tot['claims'] > 0 ? round($tot['paid_n']/$tot['claims']*100, 1) : 0;
$moneyS  = fn($v) => '$' . number_format((float)$v, 2);

require __DIR__ . '/../includes/client-portal-head.php';
?>
<style>
.ov-head{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:2px 0 8px}
.ov-head h2{margin:0;font-size:1.1rem;font-weight:800}
.ov-pill{display:inline-flex;align-items:center;gap:4px;background:var(--m-surface-2);border:1px solid var(--m-border);border-radius:999px;padding:2px 9px;font-size:.7rem;color:var(--m-text-soft)}
.kpi-strip{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;margin-bottom:8px}
@media(max-width:992px){.kpi-strip{grid-template-columns:repeat(4,1fr)}}
@media(max-width:560px){.kpi-strip{grid-template-columns:repeat(2,1fr)}}
.kpi{background:var(--m-surface);border:1px solid var(--m-border);border-radius:8px;padding:6px 10px;position:relative;overflow:hidden}
.kpi .lbl{font-size:.6rem;text-transform:uppercase;letter-spacing:.02em;color:var(--m-text-soft);white-space:nowrap}
.kpi .val{font-size:1.05rem;font-weight:700;line-height:1.1;margin-top:1px;font-variant-numeric:tabular-nums}
.kpi .bar{position:absolute;left:0;top:0;bottom:0;width:3px}
.chart-card{background:var(--m-surface);border:1px solid var(--m-border);border-radius:9px;padding:2px 4px 0;height:100%}
.chart-card .ttl{font-size:.78rem;font-weight:600;padding:6px 10px 0;color:var(--m-text)}
</style>
<div class="container-xl">
    <div class="ov-head">
        <h2><i class="ti ti-chart-histogram me-2 text-primary"></i>Hi <?= e(explode(' ', trim((string)$user['name']))[0] ?: 'there') ?> — Collection Overview</h2>
        <span class="ov-pill"><i class="ti ti-users"></i> <?= (int)$clientCt ?> client(s)</span>
        <span class="ov-pill"><i class="ti ti-report-analytics"></i> <?= (int)$reportCount ?> report(s)</span>
        <?php if ($hasData): ?><span class="ov-pill"><i class="ti ti-file-invoice"></i> <?= number_format($tot['claims']) ?> claim(s)</span><?php endif; ?>
    </div>

    <?php if (!$hasData): ?>
        <div class="card"><div class="card-body text-center text-secondary py-5">
            <div style="font-size:2.6rem"><i class="ti ti-chart-area-line"></i></div>
            <h3 class="mt-2">Your dashboard will appear here</h3>
            <p class="mb-0">Once collection data has been uploaded for <?= e($cp_brand) ?>, you'll see trends, collection rate, balances, and aging — all at a glance.</p>
        </div></div>
    <?php else: ?>
        <!-- KPI strip (same metrics as the internal AR overview) -->
        <div class="kpi-strip">
            <div class="kpi"><span class="bar" style="background:#206bc4"></span><div class="lbl">Charge</div><div class="val"><?= $moneyS($tot['charge']) ?></div></div>
            <div class="kpi"><span class="bar" style="background:#4263eb"></span><div class="lbl">Expected</div><div class="val"><?= $moneyS($tot['expected']) ?></div></div>
            <div class="kpi"><span class="bar" style="background:#2fb344"></span><div class="lbl">Total Paid</div><div class="val text-green"><?= $moneyS($tot['paid']) ?></div></div>
            <div class="kpi"><span class="bar" style="background:#f76707"></span><div class="lbl">Insurance Balance</div><div class="val text-orange"><?= $moneyS($tot['ins_balance']) ?></div></div>
            <div class="kpi"><span class="bar" style="background:#ae3ec9"></span><div class="lbl">Patient Balance</div><div class="val text-purple"><?= $moneyS($tot['pat_balance']) ?></div></div>
            <div class="kpi"><span class="bar" style="background:#7c3aed"></span><div class="lbl">Collection %</div><div class="val text-<?= $collPct>=70?'green':($collPct>=40?'orange':'red') ?>"><?= number_format($collPct,1) ?>%</div></div>
            <div class="kpi"><span class="bar" style="background:#0ca678"></span><div class="lbl">Claims</div><div class="val"><?= number_format($tot['claims']) ?></div></div>
        </div>

        <div class="row row-cards g-2">
            <div class="col-lg-8"><div class="chart-card">
                <div class="ttl"><i class="ti ti-chart-area-line me-1 text-primary"></i>Revenue &amp; A/R trend <span class="fw-normal text-secondary" style="font-size:.7rem">· Charge vs Paid vs Ins/Pat Balance by month</span></div>
                <div id="ch_trend" style="min-height:180px"></div>
            </div></div>
            <div class="col-lg-4"><div class="chart-card">
                <div class="ttl"><i class="ti ti-gauge me-1 text-primary"></i>Collection rate</div>
                <div id="ch_gauge" style="min-height:180px"></div>
            </div></div>
            <div class="col-lg-7"><div class="chart-card">
                <div class="ttl"><i class="ti ti-building me-1 text-primary"></i>Total balance by process (Ins + Pat)</div>
                <div id="ch_client"></div>
            </div></div>
            <div class="col-lg-5"><div class="chart-card">
                <div class="ttl"><i class="ti ti-clock-hour-4 me-1 text-primary"></i>Aging of open balance</div>
                <div id="ch_aging" style="min-height:180px"></div>
            </div></div>
        </div>

        <!-- Per-dashboard drill-in (the client has no sidebar to browse from) -->
        <div class="card mt-2">
            <div class="card-header"><h3 class="card-title mb-0">Your collection dashboards</h3></div>
            <div class="table-responsive"><table class="table table-vcenter card-table">
                <thead><tr><th>Dashboard</th><th>Client</th><th class="text-end">Charges</th><th class="text-end">Collected</th><th class="text-end">Outstanding</th><th class="text-end">Collected %</th><th>Updated</th><th class="w-1"></th></tr></thead>
                <tbody>
                <?php foreach ($reports as $r): $a=$byReport[(int)$r['id']]??null;
                    $ch=(float)($a['charges']??0); $co=(float)($a['collected']??0); $os=(float)($a['outstanding']??0); $pct=$ch>0?round(100*$co/$ch,1):0; ?>
                    <tr>
                        <td><strong><?= e($r['name']) ?></strong><div class="small text-secondary"><?= e($r['process_name']) ?></div></td>
                        <td class="small text-secondary"><?= e($r['client_name']) ?></td>
                        <td class="text-end font-mono"><?= $moneyS($ch) ?></td>
                        <td class="text-end font-mono text-green"><?= $moneyS($co) ?></td>
                        <td class="text-end font-mono" style="color:#f59e0b"><?= $moneyS($os) ?></td>
                        <td class="text-end fw-bold" style="color:<?= $pct>=80?'#16a34a':($pct>=50?'#f59e0b':'#dc2626') ?>"><?= $pct ?>%</td>
                        <td class="small text-secondary text-nowrap"><?= !empty($a['lu']) ? e(fmt_date($a['lu'])) : '<span class="text-secondary">—</span>' ?></td>
                        <td class="text-end"><a href="<?= e(url('/admin/collection.php?id=' . (int)$r['id'])) ?>" class="btn btn-sm btn-primary"><i class="ti ti-external-link me-1"></i>Open</a></td>
                    </tr>
                <?php endforeach; ?>
                </tbody>
            </table></div>
        </div>
    <?php endif; ?>
</div>

<?php if ($hasData): ?>
<script>
(function(){
    var M = <?= json_encode($months) ?>, C = <?= json_encode(array_values($byClient)) ?>, AG = <?= json_encode($aging) ?>;
    var money = function(v){ v=Number(v||0); var a=Math.abs(v);
        if(a>=1e6) return '$'+(v/1e6).toFixed(2)+'M'; if(a>=1e3) return '$'+(v/1e3).toFixed(1)+'K'; return '$'+Math.round(v).toLocaleString(); };
    function dark(){ return document.documentElement.getAttribute('data-bs-theme')==='dark'; }
    var axisColor=function(){return dark()?'#97a2b6':'#6c7a89';}, grid=function(){return dark()?'#1e2a44':'#eef0f3';};
    var charts=[];
    charts.push(new ApexCharts(document.getElementById('ch_trend'), {
        chart:{type:'area',height:180,toolbar:{show:false},fontFamily:'inherit',background:'transparent'},
        theme:{mode:dark()?'dark':'light'},
        series:[{name:'Charge',data:M.map(function(m){return m.charge;})},{name:'Total Paid',data:M.map(function(m){return m.paid;})},
                {name:'Insurance Balance',data:M.map(function(m){return m.ins_balance;})},{name:'Patient Balance',data:M.map(function(m){return m.pat_balance;})}],
        colors:['#206bc4','#2fb344','#f76707','#ae3ec9'], stroke:{curve:'smooth',width:2}, dataLabels:{enabled:false},
        fill:{type:'gradient',gradient:{shadeIntensity:1,opacityFrom:.4,opacityTo:.05,stops:[0,100]}},
        xaxis:{categories:M.map(function(m){return m.label;}),axisBorder:{show:false},labels:{style:{colors:axisColor()}}},
        yaxis:{tickAmount:4,forceNiceScale:true,labels:{style:{colors:axisColor()},formatter:money}},
        tooltip:{shared:true,y:{formatter:money}}, grid:{borderColor:grid()}, legend:{labels:{colors:axisColor()}}
    }));
    charts.push(new ApexCharts(document.getElementById('ch_gauge'), {
        chart:{type:'radialBar',height:180,fontFamily:'inherit',background:'transparent'}, theme:{mode:dark()?'dark':'light'},
        series:[<?= (float)$collPct ?>], labels:['Collected'],
        colors:[<?= $collPct>=70?"'#2fb344'":($collPct>=40?"'#f59f00'":"'#d63939'") ?>],
        plotOptions:{radialBar:{hollow:{size:'58%'},dataLabels:{name:{color:axisColor(),fontSize:'11px',offsetY:16},
            value:{fontSize:'20px',fontWeight:700,offsetY:-8,formatter:function(v){return v+'%';}}}}}, stroke:{lineCap:'round'}
    }));
    var clientHeight=Math.max(200,C.length*28+70);
    var clientLabels=C.map(function(c){return c.client.length>26?c.client.slice(0,24)+'…':c.client;});
    charts.push(new ApexCharts(document.getElementById('ch_client'), {
        chart:{type:'bar',height:clientHeight,toolbar:{show:false},fontFamily:'inherit',background:'transparent',stacked:true}, theme:{mode:dark()?'dark':'light'},
        series:[{name:'Insurance Balance',data:C.map(function(c){return c.ins_balance;})},{name:'Patient Balance',data:C.map(function(c){return c.pat_balance;})}],
        colors:['#f76707','#ae3ec9'], plotOptions:{bar:{horizontal:true,borderRadius:4,barHeight:'70%'}}, dataLabels:{enabled:false},
        xaxis:{categories:clientLabels,labels:{style:{colors:axisColor()},formatter:money}},
        yaxis:{labels:{style:{colors:axisColor(),fontSize:'11px'},maxWidth:200}},
        tooltip:{y:{formatter:money},x:{formatter:function(_,o){return C[o.dataPointIndex]?C[o.dataPointIndex].client:'';}}},
        grid:{borderColor:grid()}, legend:{position:'top',horizontalAlign:'right',fontSize:'11px',labels:{colors:axisColor()}}
    }));
    var agLabels=Object.keys(AG), agData=agLabels.map(function(k){return AG[k];});
    charts.push(new ApexCharts(document.getElementById('ch_aging'), {
        chart:{type:'donut',height:180,fontFamily:'inherit',background:'transparent'}, theme:{mode:dark()?'dark':'light'},
        series:agData, labels:agLabels.map(function(k){return k+' days';}),
        colors:['#2fb344','#74b816','#f59f00','#f76707','#d63939'], dataLabels:{enabled:false}, stroke:{width:2}, tooltip:{y:{formatter:money}},
        legend:{position:'right',fontSize:'9px',markers:{width:8,height:8},itemMargin:{horizontal:3,vertical:1},labels:{colors:axisColor()},
            formatter:function(name,opts){var s=opts.w.globals.series,t=0;for(var i=0;i<s.length;i++)t+=s[i];var p=t>0?Math.round(s[opts.seriesIndex]/t*100):0;return name.replace(' days','')+': '+p+'%';}},
        plotOptions:{pie:{donut:{size:'72%',labels:{show:true,name:{fontSize:'10px'},value:{fontSize:'14px',fontWeight:700,formatter:money},
            total:{show:true,label:'Total Balance',fontSize:'9px',formatter:function(w){var s=w.globals.seriesTotals,t=0;for(var i=0;i<s.length;i++)t+=s[i];return money(t);}}}}}}
    }));
    charts.forEach(function(c){c.render();});
    window.addEventListener('m-theme-change', function(){
        charts.forEach(function(c){ c.updateOptions({theme:{mode:dark()?'dark':'light'},xaxis:{labels:{style:{colors:axisColor()}}},yaxis:{labels:{style:{colors:axisColor()}}},grid:{borderColor:grid()},legend:{labels:{colors:axisColor()}}},false,false); });
    });
})();
</script>
<?php endif; ?>
<?php require __DIR__ . '/../includes/client-portal-foot.php'; ?>
