<?php
/**
 * Workspace dashboard — the visual landing for report-viewer roles (Supervisor, Manager).
 * A real charts dashboard summarising the A/R they oversee (all collection reports):
 * trend, collection %, insurance/patient balance by client, and aging — powered by ApexCharts.
 * SysAdmin keeps the full platform dashboard.
 */
require_once __DIR__ . '/../includes/config.php';
$user = require_login('internal');
require_permission('report.view');

$pdo   = db();
$first = explode(' ', trim((string)$user['name']))[0] ?: 'there';
$noAccess = (accessible_report_ids((int)$user['id']) === []);   // non-sysadmin with zero granted reports

$months = $byClient = $aging = [];
$tot = ['charge'=>0,'paid'=>0,'expected'=>0,'ins_balance'=>0,'pat_balance'=>0,'claims'=>0,'paid_n'=>0];
$clientCount = 0; $reportCount = 0; $hasData = false;

/* Restrict everything to the reports SysAdmin granted this user ('' = SysAdmin, all). */
$scopeRaw = report_scope_sql('report_id');     // collection_raw (no alias)
$scopeCr  = report_scope_sql('cr.report_id');  // collection_raw cr
$scopeRep = report_scope_sql('id');            // reports (no alias)

try {
    $reportCount = (int) $pdo->query("SELECT COUNT(*) FROM reports WHERE deleted_at IS NULL AND status='active'" . $scopeRep)->fetchColumn();

    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 total open balance (insurance + patient) — labelled "Client » Process" */
    foreach ($pdo->query("
        SELECT CONCAT(COALESCE(c.name,'(unknown)'),' » ',COALESCE(p.name,'(process)')) client,
               SUM(cr.charge_amount) charge,
               SUM(cr.primary_ins_paid + cr.secondary_ins_paid + cr.tertiary_ins_paid + cr.patient_payment_amount) paid,
               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'], 'charge'=>round((float)$r['charge'],2),
                       'paid'=>round((float)$r['paid'],2),
                       'ins_balance'=>round((float)$r['ins_balance'],2),
                       'pat_balance'=>round((float)$r['pat_balance'],2),
                       'total_balance'=>round((float)$r['ins_balance'] + (float)$r['pat_balance'],2)];
    }
    $clientCount = count($byClient);

    /* Aging: total balance (ins + pat) per bucket */
    $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;
    $hasData = $tot['charge'] > 0 || $tot['claims'] > 0;
} catch (Throwable $e) { /* collection schema not present yet */ }

// Collection % = paid claims ÷ total claims
$collPct = $tot['claims'] > 0 ? round($tot['paid_n']/$tot['claims']*100, 1) : 0;
// Tiles show the FULL accurate amount (client requirement — not K/M abbreviated).
$moneyS = function($v){ return '$' . number_format((float)$v, 2); };

$pageTitle = 'Dashboard';
require __DIR__ . '/../includes/header.php';
?>
<style>
/* page-tighten: trim the default page padding so everything fits in one view */
.page-body{padding-top:3px!important;padding-bottom:4px!important}
.ov-head{display:flex;flex-wrap:wrap;align-items:center;gap:5px;margin:3px 0 4px}
.ov-head h2{margin:0;font-size:.92rem;font-weight:700}
.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:1px 7px;font-size:.64rem;color:var(--m-text-soft)}
.kpi-strip{display:grid;grid-template-columns:repeat(7,1fr);gap:5px;margin-bottom:5px}
@media(max-width:992px){.kpi-strip{grid-template-columns:repeat(4,1fr)}}
@media(max-width:560px){.kpi-strip{grid-template-columns:repeat(3,1fr)}}
.kpi{background:var(--m-surface);border:1px solid var(--m-border);border-radius:7px;padding:3px 8px;position:relative;overflow:hidden}
.kpi .lbl{font-size:.54rem;text-transform:uppercase;letter-spacing:.02em;color:var(--m-text-soft);white-space:nowrap}
.kpi .val{font-size:.95rem;font-weight:700;line-height:1.05;margin-top:0;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:1px 4px 0;height:100%}
.chart-card .ttl{font-size:.72rem;font-weight:600;padding:4px 8px 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($first) ?> — Collection Overview</h2>
        <span class="ov-pill"><i class="ti ti-shield-check"></i> <?= e($myRoleLabel) ?></span>
        <span class="ov-pill"><i class="ti ti-users"></i> <?= (int)$clientCount ?> client(s)</span>
        <span class="ov-pill"><i class="ti ti-report-analytics"></i> <?= (int)$reportCount ?> reports</span>
        <div class="btn-group btn-group-sm ms-auto me-2" role="group" aria-label="Dashboard view">
            <button type="button" class="btn btn-primary" disabled><i class="ti ti-chart-area-line me-1"></i>Trend view</button>
            <a href="<?= e(url('/admin/workspace-v2.php')) ?>" class="btn btn-outline-primary"><i class="ti ti-target me-1"></i>Operations view</a>
        </div>
        <a href="<?= e(url('/admin/reports.php')) ?>" class="btn btn-sm btn-outline-primary"><i class="ti ti-search me-1"></i>Browse reports</a>
    </div>
</div>

<div class="page-body"><div class="container-xl">
<?php if (!$hasData): ?>
    <div class="card"><div class="card-body text-center text-secondary py-5">
        <?php if ($noAccess): ?>
            <div style="font-size:2.6rem">🔒</div>
            <h3 class="mt-2">No reports assigned yet</h3>
            <p class="mb-0">You haven't been granted access to any reports. Ask your administrator to assign reports to you under <strong>Report Access</strong>.</p>
        <?php else: ?>
            <div style="font-size:2.6rem">📈</div>
            <h3 class="mt-2">Your dashboard will appear here</h3>
            <p class="mb-0">Once collection data has been uploaded, you'll see trends, collection rate, balances by client, and aging — all at a glance.</p>
        <?php endif; ?>
    </div></div>
<?php else: ?>

    <!-- KPI strip -->
    <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" title="Open insurance balance (still owed by insurance)">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" title="Open patient balance (still owed by patient)">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:.68rem">· Charge vs Paid vs Ins/Pat Balance by month</span></div>
                <div id="ch_trend" style="min-height:170px"></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:170px"></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:170px"></div>
            </div>
        </div>
    </div>
<?php endif; ?>
</div></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'; };
    var grid = function(){ return dark()?'#1e2a44':'#eef0f3'; };
    var charts = [];

    // 1) Trend area
    charts.push(new ApexCharts(document.getElementById('ch_trend'), {
        chart:{type:'area',height:170,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()}}
    }));

    // 2) Collection % radial gauge
    charts.push(new ApexCharts(document.getElementById('ch_gauge'), {
        chart:{type:'radialBar',height:170,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'}
    }));

    // 3) Balance by client (horizontal stacked bar — insurance + patient)
    /* 28 px per client row + ~70 px for legend / x-axis, with a 200 px floor so a
       single-client chart still has breathing room.  Long names truncated to 26
       chars (full name visible in the tooltip). */
    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()}}
    }));

    // 4) Aging donut
    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:170,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/footer.php'; ?>
