# ============================================================
# MODULE 1: PBZO Compliance & VBS Dashboard
# Phoenix Metals HSEQ VBS — Separate module file
# Imported by app.py before Flask app starts
# ============================================================

import sqlite3
from datetime import datetime, timedelta
from search import get_db
from indexer import DB_PATH
from flask import jsonify, request


def init_db_pbzo():
    """Create PBZO/VBS tables if not exist."""
    try:
        conn = sqlite3.connect(DB_PATH)
        c = conn.cursor()
        c.execute('''CREATE TABLE IF NOT EXISTS vbs_elements (
            id INTEGER PRIMARY KEY, code TEXT NOT NULL UNIQUE,
            name_nl TEXT NOT NULL, name_en TEXT NOT NULL,
            description TEXT, sort_order INTEGER)''')
        c.execute('''CREATE TABLE IF NOT EXISTS legislation_register (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            legislation_type TEXT NOT NULL, legislation_code TEXT NOT NULL,
            title TEXT NOT NULL, article TEXT, version_date TEXT,
            source_url TEXT, document_id INTEGER, active INTEGER DEFAULT 1,
            created_date TEXT DEFAULT CURRENT_TIMESTAMP,
            updated_date TEXT DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (document_id) REFERENCES documents(id) ON DELETE SET NULL)''')
        c.execute('''CREATE TABLE IF NOT EXISTS pbzo_requirements (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            requirement_code TEXT NOT NULL UNIQUE, title TEXT NOT NULL,
            description TEXT NOT NULL, vbs_element_id INTEGER NOT NULL,
            legislation_id INTEGER, requirement_source TEXT,
            severity TEXT DEFAULT 'mandatory', applicability TEXT DEFAULT 'phoenix_metals',
            frequency TEXT, created_date TEXT DEFAULT CURRENT_TIMESTAMP,
            updated_date TEXT DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (vbs_element_id) REFERENCES vbs_elements(id),
            FOREIGN KEY (legislation_id) REFERENCES legislation_register(id) ON DELETE SET NULL)''')
        c.execute('''CREATE TABLE IF NOT EXISTS compliance_status (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            pbzo_requirement_id INTEGER NOT NULL,
            status TEXT DEFAULT 'not_assessed', compliance_score REAL DEFAULT 0.0,
            evidence_document_id INTEGER, evidence_action_id INTEGER,
            gap_description TEXT, remediation_plan TEXT,
            owner TEXT NOT NULL DEFAULT 'HSEQ Manager', assessor TEXT,
            assessment_date TEXT, next_review_date TEXT,
            priority TEXT DEFAULT 'medium', notes TEXT,
            created_date TEXT DEFAULT CURRENT_TIMESTAMP,
            updated_date TEXT DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (pbzo_requirement_id) REFERENCES pbzo_requirements(id) ON DELETE CASCADE,
            FOREIGN KEY (evidence_document_id) REFERENCES documents(id) ON DELETE SET NULL,
            FOREIGN KEY (evidence_action_id) REFERENCES action_items(id) ON DELETE SET NULL)''')
        c.execute('''CREATE TABLE IF NOT EXISTS inspection_schedule (
            id INTEGER PRIMARY KEY AUTOINCREMENT, equipment_tag TEXT,
            inspection_type TEXT NOT NULL, legislation_id INTEGER,
            description TEXT NOT NULL, frequency_days INTEGER NOT NULL,
            last_done_date TEXT, next_due_date TEXT,
            status TEXT DEFAULT 'scheduled', certified_body TEXT,
            owner TEXT, related_vka_id INTEGER, notes TEXT,
            created_date TEXT DEFAULT CURRENT_TIMESTAMP,
            updated_date TEXT DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (legislation_id) REFERENCES legislation_register(id) ON DELETE SET NULL,
            FOREIGN KEY (related_vka_id) REFERENCES vka_items(id) ON DELETE SET NULL)''')
        c.execute('CREATE INDEX IF NOT EXISTS idx_cs_req ON compliance_status(pbzo_requirement_id)')
        c.execute('CREATE INDEX IF NOT EXISTS idx_pbzo_vbs ON pbzo_requirements(vbs_element_id)')
        c.execute('CREATE INDEX IF NOT EXISTS idx_insp_due ON inspection_schedule(next_due_date, status)')
        count = c.execute("SELECT COUNT(*) FROM vbs_elements").fetchone()[0]
        if count == 0:
            c.executemany("INSERT OR IGNORE INTO vbs_elements VALUES (?,?,?,?,?,?)", [
                (1,'VBS-01','Organisatie & Beleid','Organization & Policy','Veiligheidsbeleid, rollen, verantwoordelijkheden, M&A structuur',1),
                (2,'VBS-02','Risico-identificatie & Evaluatie','Hazard Identification & Risk Evaluation','RI&E, HAZOP, QRA, bronidentificatie',2),
                (3,'VBS-03','Operationele Beheersing','Operational Control','Procedures, werkplekinstructies, PTW, MOC',3),
                (4,'VBS-04','Noodplan & Respons','Emergency Response','Noodplannen, interne hulpverlening, alarmieren, oefeningen',4),
                (5,'VBS-05','Kwalificaties & Competenties','Competence & Training','Trainingmatrix, certificering, VCA, instructie',5),
                (6,'VBS-06','Monitoring & Meting','Monitoring & Measurement','KPIs, inspecties, emissiemetingen, audits',6),
                (7,'VBS-07','Continue Verbetering','Continuous Improvement','Incidenten, afwijkingen, correcties, management review',7),
            ])
        conn.commit(); conn.close()
    except Exception as e:
        print(f"[WARN] init_db_pbzo: {e}")


def register_pbzo_routes(app, page_fn, BASE_PATH="/hseq-dashboard"):
    """Register all PBZO/VBS routes on the Flask app."""

    _page = page_fn

    @app.route(BASE_PATH + '/api/pbzo/dashboard')
    def api_pbzo_dashboard():
        db = get_db()
        try:
            rows = db.execute("""
                SELECT ve.id AS vbs_id, ve.code AS vbs_code, ve.name_nl AS vbs_name,
                    COUNT(pr.id) AS total_requirements,
                    SUM(CASE WHEN cs.status = 'compliant' THEN 1 ELSE 0 END) AS compliant_count,
                    SUM(CASE WHEN cs.status = 'partially_compliant' THEN 1 ELSE 0 END) AS partial_count,
                    SUM(CASE WHEN cs.status = 'non_compliant' THEN 1 ELSE 0 END) AS non_compliant_count,
                    SUM(CASE WHEN cs.status IN ('not_assessed','non_compliant') OR cs.status IS NULL THEN 1 ELSE 0 END) AS gap_count,
                    ROUND(AVG(COALESCE(cs.compliance_score, 0)), 1) AS avg_score
                FROM vbs_elements ve
                LEFT JOIN pbzo_requirements pr ON pr.vbs_element_id = ve.id
                LEFT JOIN compliance_status cs ON cs.pbzo_requirement_id = pr.id
                GROUP BY ve.id ORDER BY ve.sort_order
            """).fetchall()
            overall = db.execute("SELECT ROUND(AVG(COALESCE(compliance_score,0)),1) AS s FROM compliance_status").fetchone()
            overdue = db.execute("SELECT COUNT(*) AS c FROM inspection_schedule WHERE next_due_date < date('now') AND status='scheduled'").fetchone()
            return jsonify({
                'timestamp': datetime.utcnow().isoformat(),
                'overall_score': overall['s'] if overall and overall['s'] else 0,
                'overdue_inspections': overdue['c'] if overdue else 0,
                'vbs_elements': [dict(r) for r in rows]
            })
        finally:
            db.close()

    @app.route(BASE_PATH + '/api/pbzo/vbs/<int:vbs_id>')
    def api_pbzo_vbs_detail(vbs_id):
        db = get_db()
        try:
            vbs = db.execute("SELECT * FROM vbs_elements WHERE id=?", (vbs_id,)).fetchone()
            if not vbs:
                return jsonify({'error':'VBS element not found'}), 404
            reqs = db.execute("""
                SELECT pr.id, pr.requirement_code, pr.title, pr.description,
                    pr.requirement_source, pr.severity, pr.frequency,
                    cs.status, cs.compliance_score, cs.gap_description, cs.remediation_plan,
                    cs.owner, cs.assessor, cs.assessment_date, cs.next_review_date, cs.priority,
                    d.title AS evidence_document_title,
                    ai.title AS evidence_action_title, ai.status AS evidence_action_status,
                    lr.legislation_code, lr.title AS legislation_title, lr.article AS legislation_article
                FROM pbzo_requirements pr
                LEFT JOIN compliance_status cs ON cs.pbzo_requirement_id = pr.id
                LEFT JOIN documents d ON cs.evidence_document_id = d.id
                LEFT JOIN action_items ai ON cs.evidence_action_id = ai.id
                LEFT JOIN legislation_register lr ON pr.legislation_id = lr.id
                WHERE pr.vbs_element_id = ?
                ORDER BY CASE cs.priority WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 ELSE 4 END, pr.requirement_code
            """, (vbs_id,)).fetchall()
            return jsonify({'vbs_element': dict(vbs), 'requirements': [dict(r) for r in reqs], 'total': len(reqs)})
        finally:
            db.close()

    @app.route(BASE_PATH + '/api/pbzo/status', methods=['POST'])
    def api_pbzo_status_update():
        data = request.get_json()
        if not data or 'pbzo_requirement_id' not in data:
            return jsonify({'error': 'pbzo_requirement_id required'}), 400
        db = get_db()
        try:
            now = datetime.utcnow().isoformat()
            nxt = (datetime.utcnow() + timedelta(days=365)).strftime('%Y-%m-%d')
            existing = db.execute("SELECT id FROM compliance_status WHERE pbzo_requirement_id=?", (data['pbzo_requirement_id'],)).fetchone()
            if existing:
                db.execute("""UPDATE compliance_status SET status=?, compliance_score=?, gap_description=?, remediation_plan=?,
                    owner=?, assessor=?, evidence_document_id=?, evidence_action_id=?, priority=?, notes=?,
                    assessment_date=?, next_review_date=?, updated_date=? WHERE pbzo_requirement_id=?""",
                    (data.get('status','not_assessed'), data.get('compliance_score',0), data.get('gap_description'),
                     data.get('remediation_plan'), data.get('owner','HSEQ Manager'), data.get('assessor'),
                     data.get('evidence_document_id'), data.get('evidence_action_id'), data.get('priority','medium'),
                     data.get('notes'), now, nxt, now, data['pbzo_requirement_id']))
            else:
                db.execute("""INSERT INTO compliance_status (pbzo_requirement_id, status, compliance_score, gap_description,
                    remediation_plan, owner, assessor, evidence_document_id, evidence_action_id, priority, notes,
                    assessment_date, next_review_date) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)""",
                    (data['pbzo_requirement_id'], data.get('status','not_assessed'), data.get('compliance_score',0),
                     data.get('gap_description'), data.get('remediation_plan'), data.get('owner','HSEQ Manager'),
                     data.get('assessor'), data.get('evidence_document_id'), data.get('evidence_action_id'),
                     data.get('priority','medium'), data.get('notes'), now, nxt))
            db.commit()
            return jsonify({'success': True, 'updated_date': now})
        except Exception as e:
            db.rollback()
            return jsonify({'error': str(e)}), 500
        finally:
            db.close()

    @app.route(BASE_PATH + '/api/pbzo/gaps')
    def api_pbzo_gaps():
        db = get_db()
        try:
            gaps = db.execute("""
                SELECT cs.id, pr.requirement_code, pr.title AS requirement_title, pr.requirement_source,
                    ve.code AS vbs_code, ve.name_nl AS vbs_name,
                    cs.status, cs.compliance_score, cs.gap_description, cs.remediation_plan,
                    cs.owner, cs.priority, cs.assessment_date, cs.next_review_date,
                    ai.id AS linked_action_id, ai.title AS linked_action_title
                FROM compliance_status cs
                JOIN pbzo_requirements pr ON cs.pbzo_requirement_id = pr.id
                JOIN vbs_elements ve ON pr.vbs_element_id = ve.id
                LEFT JOIN action_items ai ON cs.evidence_action_id = ai.id
                WHERE cs.status IN ('non_compliant','partially_compliant','not_assessed')
                ORDER BY CASE cs.priority WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 ELSE 4 END,
                    cs.compliance_score ASC
            """).fetchall()
            summary = db.execute("""
                SELECT pr.requirement_source, COUNT(*) AS gap_count, ROUND(AVG(cs.compliance_score),1) AS avg_score
                FROM compliance_status cs JOIN pbzo_requirements pr ON cs.pbzo_requirement_id = pr.id
                WHERE cs.status IN ('non_compliant','partially_compliant','not_assessed')
                GROUP BY pr.requirement_source ORDER BY avg_score ASC
            """).fetchall()
            return jsonify({'gaps': [dict(r) for r in gaps], 'total_gaps': len(gaps), 'summary_by_source': [dict(r) for r in summary]})
        finally:
            db.close()

    @app.route(BASE_PATH + '/pbzo')
    def pbzo_dashboard_page():
        body = '''
        <style>
        .pbzo-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:20px}
        .pbzo-score-ring{width:140px;height:140px;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 8px;font-size:32px;font-weight:700;border:6px solid #E9ECEF}
        .pbzo-score-ring.green{border-color:#2B8A3E;color:#2B8A3E}
        .pbzo-score-ring.orange{border-color:#E67700;color:#E67700}
        .pbzo-score-ring.red{border-color:#E03131;color:#E03131}
        .pbzo-score-ring.gray{border-color:#868E96;color:#868E96}
        .vbs-card{background:#fff;border-radius:10px;padding:16px;box-shadow:0 1px 3px rgba(0,0,0,.06);border:1px solid #E9ECEF;cursor:pointer;transition:all .2s;border-left:4px solid #003366}
        .vbs-card:hover{box-shadow:0 4px 12px rgba(0,0,0,.1);transform:translateY(-2px)}
        .vbs-card .vbs-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
        .vbs-card .vbs-title{font-size:13px;font-weight:600;color:#003366}
        .vbs-card .vbs-code{font-size:10px;color:#868E96;font-weight:500}
        .vbs-bar{height:6px;background:#E9ECEF;border-radius:3px;overflow:hidden;margin-top:6px}
        .vbs-bar-fill{height:100%;border-radius:3px;transition:width .6s}
        .rag-green{background:#2B8A3E}.rag-orange{background:#E67700}.rag-red{background:#E03131}.rag-gray{background:#868E96}
        .badge-compliant{background:#D3F9D8;color:#2B8A3E}.badge-partially_compliant{background:#FFF3BF;color:#E67700}
        .badge-non_compliant{background:#FFE3E3;color:#E03131}.badge-not_assessed{background:#E9ECEF;color:#495057}
        .badge-mandatory{background:#D0EBFF;color:#1864AB}.badge-recommended{background:#E5DBFF;color:#6741D9}
        .badge-brzo{background:#FFE3E3;color:#C92A2A}.badge-arbowet{background:#D3F9D8;color:#2B8A3E}
        .badge-omgevingswet{background:#D0EBFF;color:#1864AB}.badge-pgs{background:#FFF3BF;color:#E67700}
        .badge-iso45001{background:#E5DBFF;color:#6741D9}.badge-internal{background:#F1F3F5;color:#495057}
        .badge-not_applicable{background:#F1F3F5;color:#868E96}
        .req-row{display:flex;align-items:center;gap:10px;padding:12px;border-bottom:1px solid #E9ECEF;transition:background .2s;flex-wrap:wrap}
        .req-row:hover{background:#F8F9FA}
        .req-row .req-code{font-size:10px;color:#868E96;min-width:110px;font-family:monospace}
        .req-row .req-title{font-size:13px;font-weight:500;flex:1;min-width:200px}
        .req-row .req-freq{font-size:10px;color:#868E96;min-width:70px}
        .req-actions{display:flex;gap:4px;flex-shrink:0}
        .modal-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1000;align-items:center;justify-content:center}
        .modal-overlay.active{display:flex}
        .modal-box{background:#fff;border-radius:12px;padding:24px;max-width:560px;width:90%;max-height:80vh;overflow-y:auto}
        .modal-box h3{font-size:16px;font-weight:600;color:#003366;margin-bottom:16px}
        .modal-box label{font-size:12px;font-weight:500;color:#495057;display:block;margin-bottom:4px;margin-top:12px}
        .modal-box select,.modal-box input,.modal-box textarea{width:100%;padding:8px 12px;border:1px solid #DEE2E6;border-radius:6px;font-size:13px;font-family:inherit}
        .modal-box textarea{min-height:60px;resize:vertical}
        .radar-container{max-width:420px;margin:0 auto}
        .gap-info{margin:0 0 4px 120px;font-size:11px}
        @media(max-width:768px){.pbzo-grid{grid-template-columns:1fr}.radar-container{max-width:100%}.req-row{flex-direction:column;align-items:flex-start}.req-actions{width:100%}.gap-info{margin-left:0}}
        </style>

        <div class="stats-grid" id="topStats"></div>

        <div class="card">
          <h2>&#x1F4E1; VBS Compliance Radar &#8212; Bijlage III Seveso III</h2>
          <div class="radar-container"><canvas id="radarChart"></canvas></div>
        </div>

        <div class="card">
          <h2>&#x1F3D7;&#xFE0F; VBS-Elementen &#8212; Klik voor details</h2>
          <div class="pbzo-grid" id="vbsCards"></div>
        </div>

        <div class="card" id="detailCard" style="display:none">
          <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px">
            <h2 id="detailTitle" style="margin:0">Details</h2>
            <button class="btn btn-sm btn-outline" onclick="closeDetail()">&#x2715; Sluiten</button>
          </div>
          <div id="detailBody"></div>
        </div>

        <div class="modal-overlay" id="modalEvidence">
          <div class="modal-box">
            <h3>&#x1F4C4; Koppel Bewijsdocument</h3>
            <p id="modalEvidenceReq" style="font-size:12px;color:#868E96;margin-bottom:12px"></p>
            <label>Document ID (uit kennisbank):</label>
            <input id="evidenceDocId" type="number" placeholder="Bijv. 99">
            <label>Toelichting:</label>
            <textarea id="evidenceNotes" placeholder="Optionele toelichting..."></textarea>
            <div style="display:flex;gap:8px;margin-top:16px;justify-content:flex-end">
              <button class="btn btn-outline" onclick="closeModal('modalEvidence')">Annuleren</button>
              <button class="btn btn-primary" onclick="saveEvidence()">&#x1F4BE; Opslaan</button>
            </div>
            <input type="hidden" id="evidenceReqId">
          </div>
        </div>

        <div class="modal-overlay" id="modalAction">
          <div class="modal-box">
            <h3>&#x1F4CB; Nieuw Actiepunt</h3>
            <p id="modalActionReq" style="font-size:12px;color:#868E96;margin-bottom:12px"></p>
            <label>Titel:</label>
            <input id="actionTitle" placeholder="Actiepunt titel...">
            <label>Beschrijving:</label>
            <textarea id="actionDesc" placeholder="Wat moet er gebeuren..."></textarea>
            <label>Deadline:</label>
            <input type="date" id="actionDue">
            <label>Prioriteit:</label>
            <select id="actionPriority"><option value="critical">Critical</option><option value="high">High</option><option value="medium" selected>Medium</option><option value="low">Low</option></select>
            <div style="display:flex;gap:8px;margin-top:16px;justify-content:flex-end">
              <button class="btn btn-outline" onclick="closeModal('modalAction')">Annuleren</button>
              <button class="btn btn-primary" onclick="saveAction()">&#x1F4BE; Aanmaken</button>
            </div>
            <input type="hidden" id="actionReqId">
          </div>
        </div>

        <div class="modal-overlay" id="modalStatus">
          <div class="modal-box">
            <h3>&#x270F;&#xFE0F; Update Compliance Status</h3>
            <p id="modalStatusReq" style="font-size:12px;color:#868E96;margin-bottom:12px"></p>
            <label>Status:</label>
            <select id="statusValue" onchange="autoScore()">
              <option value="compliant">&#x2705; Compliant</option>
              <option value="partially_compliant">&#x1F7E0; Gedeeltelijk Compliant</option>
              <option value="non_compliant">&#x1F534; Niet Compliant</option>
              <option value="not_applicable">&#x2B1C; Niet van Toepassing</option>
              <option value="not_assessed">&#x26AA; Niet Beoordeeld</option>
            </select>
            <label>Compliance Score (0-100):</label>
            <input type="number" id="statusScore" min="0" max="100" value="0">
            <label>Gap Beschrijving:</label>
            <textarea id="statusGap" placeholder="Wat ontbreekt er..."></textarea>
            <label>Verbeterplan:</label>
            <textarea id="statusPlan" placeholder="Hoe gaan we dit oplossen..."></textarea>
            <label>Assessor:</label>
            <input id="statusAssessor" placeholder="Naam beoordelaar...">
            <label>Prioriteit:</label>
            <select id="statusPriority"><option value="critical">Critical</option><option value="high">High</option><option value="medium" selected>Medium</option><option value="low">Low</option></select>
            <div style="display:flex;gap:8px;margin-top:16px;justify-content:flex-end">
              <button class="btn btn-outline" onclick="closeModal('modalStatus')">Annuleren</button>
              <button class="btn btn-primary" onclick="saveStatus()">&#x1F4BE; Opslaan</button>
            </div>
            <input type="hidden" id="statusReqId">
          </div>
        </div>

        <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
        <script>
        const BP = '{{BASE_PATH}}';
        let dashboardData = null;
        let radarChart = null;
        let currentVbsId = null;

        async function loadDashboard() {
          const r = await fetch(BP+'/api/pbzo/dashboard');
          dashboardData = await r.json();
          renderTopStats(dashboardData);
          renderRadar(dashboardData);
          renderVBSCards(dashboardData);
        }

        function renderTopStats(d) {
          const el = document.getElementById('topStats');
          const scoreClass = d.overall_score >= 80 ? 'green' : d.overall_score >= 50 ? 'orange' : d.overall_score > 0 ? 'red' : 'gray';
          const totalReqs = d.vbs_elements.reduce((s,e)=>s+(e.total_requirements||0),0);
          const totalGaps = d.vbs_elements.reduce((s,e)=>s+(e.gap_count||0),0);
          el.innerHTML =
            '<div class="stat-card"><div class="pbzo-score-ring '+scoreClass+'">'+(d.overall_score||0)+'%</div><div class="label">Overall Compliance</div></div>'+
            '<div class="stat-card"><div class="value">'+totalReqs+'</div><div class="label">Totaal PBZO Eisen</div></div>'+
            '<div class="stat-card"><div class="value" style="color:'+(totalGaps>0?'#E03131':'#2B8A3E')+'">'+totalGaps+'</div><div class="label">Open Gaps</div></div>'+
            '<div class="stat-card"><div class="value" style="color:'+(d.overdue_inspections>0?'#E03131':'#2B8A3E')+'">'+d.overdue_inspections+'</div><div class="label">Verlopen Keuringen</div></div>';
        }

        function renderRadar(d) {
          const ctx = document.getElementById('radarChart').getContext('2d');
          if (radarChart) radarChart.destroy();
          const colors = d.vbs_elements.map(e => {
            const s = e.avg_score || 0;
            return s >= 80 ? '#2B8A3E' : s >= 50 ? '#E67700' : s > 0 ? '#E03131' : '#868E96';
          });
          radarChart = new Chart(ctx, {
            type: 'radar',
            data: {
              labels: d.vbs_elements.map(e => e.vbs_code + ' - ' + e.vbs_name.split(' & ')[0]),
              datasets: [{
                label: 'Compliance %',
                data: d.vbs_elements.map(e => e.avg_score || 0),
                backgroundColor: 'rgba(0,51,102,0.12)',
                borderColor: '#003366',
                borderWidth: 2,
                pointBackgroundColor: colors,
                pointRadius: 6, pointHoverRadius: 9
              },{
                label: 'Target',
                data: [100,100,100,100,100,100,100],
                backgroundColor: 'rgba(43,138,62,0.04)',
                borderColor: 'rgba(43,138,62,0.25)',
                borderWidth: 1, borderDash: [5,5], pointRadius: 0
              }]
            },
            options: {
              responsive: true,
              scales: { r: { beginAtZero: true, max: 100, ticks: { stepSize: 20, font: {size:10} }, pointLabels: { font: {size:9, weight:'600'}, color:'#003366' }, grid: {color:'#E9ECEF'} } },
              plugins: { legend: { display: false } },
              onClick: function(evt, items) {
                if (items.length > 0) {
                  var idx = items[0].index;
                  loadDetail(d.vbs_elements[idx].vbs_id);
                }
              }
            }
          });
        }

        function renderVBSCards(d) {
          const el = document.getElementById('vbsCards');
          el.innerHTML = d.vbs_elements.map(e => {
            const score = e.avg_score || 0;
            const color = score >= 80 ? '#2B8A3E' : score >= 50 ? '#E67700' : score > 0 ? '#E03131' : '#868E96';
            const gapCount = e.gap_count || 0;
            return '<div class="vbs-card" onclick="loadDetail('+e.vbs_id+')" style="border-left-color:'+color+'">'+
              '<div class="vbs-header"><span class="vbs-title">'+e.vbs_name+'</span><span class="vbs-code">'+e.vbs_code+'</span></div>'+
              '<div style="display:flex;justify-content:space-between;font-size:11px;color:#868E96"><span>'+(e.total_requirements||0)+' eisen</span><span style="font-weight:600;color:'+color+'">'+score+'%</span></div>'+
              '<div class="vbs-bar"><div class="vbs-bar-fill" style="width:'+score+'%;background:'+color+'"></div></div>'+
              (gapCount > 0 ? '<div style="font-size:10px;color:#E03131;margin-top:4px">&#x26A0;&#xFE0F; '+gapCount+' gaps</div>' : '<div style="font-size:10px;color:#2B8A3E;margin-top:4px">&#x2705; Op orde</div>')+
            '</div>';
          }).join('');
        }

        async function loadDetail(vbsId) {
          currentVbsId = vbsId;
          const r = await fetch(BP+'/api/pbzo/vbs/' + vbsId);
          const data = await r.json();
          const card = document.getElementById('detailCard');
          document.getElementById('detailTitle').textContent = data.vbs_element.code + ' \u2014 ' + data.vbs_element.name_nl;
          const el = document.getElementById('detailBody');
          if (!data.requirements.length) {
            el.innerHTML = '<div class="empty-state"><div class="emoji">&#x1F4CB;</div><h3>Geen eisen gevonden</h3><p>Dit VBS-element heeft nog geen gekoppelde PBZO-eisen.</p></div>';
            card.style.display = 'block'; card.scrollIntoView({behavior:'smooth'}); return;
          }
          el.innerHTML = data.requirements.map(req => {
            const st = req.status || 'not_assessed';
            const stLabels = {compliant:'&#x2705; Compliant',partially_compliant:'&#x1F7E0; Gedeeltelijk',non_compliant:'&#x1F534; Niet Compliant',not_applicable:'&#x2B1C; N.v.t.',not_assessed:'&#x26AA; Niet beoordeeld'};
            const stLabel = stLabels[st] || st;
            const srcLabel = req.requirement_source || 'other';
            const titleSafe = (req.requirement_code + ' ' + req.title).replace(/'/g, "\\'");
            return '<div class="req-row">'+
              '<span class="req-code">'+req.requirement_code+'</span>'+
              '<span class="req-title">'+req.title+'</span>'+
              '<span class="badge badge-'+srcLabel+'" style="font-size:9px">'+srcLabel+'</span>'+
              '<span class="badge badge-'+st+'" style="cursor:pointer" onclick="openStatusModal('+req.id+',\\''+titleSafe+'\\',\\''+st+'\\','+(req.compliance_score||0)+')">'+stLabel+'</span>'+
              '<div class="req-actions">'+
                '<button class="btn btn-sm btn-outline" onclick="openEvidenceModal('+req.id+',\\''+titleSafe+'\\')">&#x1F4C4; Bewijs</button>'+
                '<button class="btn btn-sm btn-outline" onclick="openActionModal('+req.id+',\\''+titleSafe+'\\')">&#x1F4CB; Actie</button>'+
              '</div></div>'+
              (req.gap_description ? '<div class="gap-info" style="color:#E03131">&#x26A1; Gap: '+req.gap_description+'</div>' : '')+
              (req.evidence_document_title ? '<div class="gap-info" style="color:#2B8A3E">&#x1F4CE; '+req.evidence_document_title+'</div>' : '')+
              (req.evidence_action_title ? '<div class="gap-info" style="color:#1864AB">&#x1F4CB; Actie: '+req.evidence_action_title+' ('+(req.evidence_action_status||'-')+')</div>' : '');
          }).join('');
          card.style.display = 'block'; card.scrollIntoView({behavior:'smooth'});
        }

        function closeDetail() { document.getElementById('detailCard').style.display = 'none'; }

        function openModal(id) { document.getElementById(id).classList.add('active'); }
        function closeModal(id) { document.getElementById(id).classList.remove('active'); }

        function openEvidenceModal(reqId, label) {
          document.getElementById('evidenceReqId').value = reqId;
          document.getElementById('modalEvidenceReq').textContent = label;
          document.getElementById('evidenceDocId').value = '';
          document.getElementById('evidenceNotes').value = '';
          openModal('modalEvidence');
        }

        async function saveEvidence() {
          const reqId = parseInt(document.getElementById('evidenceReqId').value);
          const docId = parseInt(document.getElementById('evidenceDocId').value);
          if (!docId) { alert('Vul een Document ID in'); return; }
          const r = await fetch(BP+'/api/pbzo/status', {
            method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({pbzo_requirement_id: reqId, evidence_document_id: docId, notes: document.getElementById('evidenceNotes').value})
          });
          const d = await r.json();
          if (d.success) { closeModal('modalEvidence'); loadDashboard(); if (currentVbsId) loadDetail(currentVbsId); }
          else alert('Fout: ' + (d.error||'onbekend'));
        }

        function openActionModal(reqId, label) {
          document.getElementById('actionReqId').value = reqId;
          document.getElementById('modalActionReq').textContent = label;
          document.getElementById('actionTitle').value = '';
          document.getElementById('actionDesc').value = '';
          document.getElementById('actionDue').value = '';
          document.getElementById('actionPriority').value = 'medium';
          openModal('modalAction');
        }

        async function saveAction() {
          const reqId = parseInt(document.getElementById('actionReqId').value);
          const title = document.getElementById('actionTitle').value.trim();
          if (!title) { alert('Titel is verplicht'); return; }
          const ar = await fetch(BP+'/api/compliance', {
            method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({title: title, description: document.getElementById('actionDesc').value, priority: document.getElementById('actionPriority').value, due_date: document.getElementById('actionDue').value, category: 'PBZO Compliance'})
          });
          const ad = await ar.json();
          if (ad.id) {
            const r = await fetch(BP+'/api/pbzo/status', {
              method:'POST', headers:{'Content-Type':'application/json'},
              body: JSON.stringify({pbzo_requirement_id: reqId, evidence_action_id: ad.id})
            });
            const d = await r.json();
            if (d.success) { closeModal('modalAction'); loadDashboard(); if (currentVbsId) loadDetail(currentVbsId); return; }
          }
          alert('Fout bij aanmaken actiepunt');
        }

        function openStatusModal(reqId, label, currentStatus, currentScore) {
          document.getElementById('statusReqId').value = reqId;
          document.getElementById('modalStatusReq').textContent = label;
          document.getElementById('statusValue').value = currentStatus;
          document.getElementById('statusScore').value = currentScore;
          document.getElementById('statusGap').value = '';
          document.getElementById('statusPlan').value = '';
          document.getElementById('statusAssessor').value = '';
          document.getElementById('statusPriority').value = 'medium';
          openModal('modalStatus');
        }

        function autoScore() {
          const scores = {compliant:100, partially_compliant:50, non_compliant:0, not_applicable:100, not_assessed:0};
          document.getElementById('statusScore').value = scores[document.getElementById('statusValue').value] || 0;
        }

        async function saveStatus() {
          const reqId = parseInt(document.getElementById('statusReqId').value);
          const r = await fetch(BP+'/api/pbzo/status', {
            method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({
              pbzo_requirement_id: reqId,
              status: document.getElementById('statusValue').value,
              compliance_score: parseFloat(document.getElementById('statusScore').value),
              gap_description: document.getElementById('statusGap').value,
              remediation_plan: document.getElementById('statusPlan').value,
              assessor: document.getElementById('statusAssessor').value,
              priority: document.getElementById('statusPriority').value
            })
          });
          const d = await r.json();
          if (d.success) { closeModal('modalStatus'); loadDashboard(); if (currentVbsId) loadDetail(currentVbsId); }
          else alert('Fout: ' + (d.error||'onbekend'));
        }

        document.querySelectorAll('.modal-overlay').forEach(function(m) {
          m.addEventListener('click', function(e) { if (e.target === m) m.classList.remove('active'); });
        });

        loadDashboard();
        </script>
        '''
        return _page(body, page_title='PBZO Compliance Tracker', page_subtitle='Phoenix Metals \u2014 Veiligheidsbeheersysteem (VBS) Module 1', active='pbzo')
