<?php
require_once __DIR__ . '/../config.php';
if (!defined('MDW_ADMIN_VERSION')) define('MDW_ADMIN_VERSION', '1.0.6');

function mdw_start_admin(): void {
    if (session_status() !== PHP_SESSION_ACTIVE) {
        session_name('MDWADMINSESSID');
        session_start();
    }
}

function mdw_is_logged_in(): bool {
    mdw_start_admin();
    return !empty($_SESSION['mdw_admin_logged_in']);
}

function mdw_require_login(): void {
    if (!mdw_is_logged_in()) {
        header('Location: login.php');
        exit;
    }
}

function mdw_h(string $value): string {
    return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}

function mdw_read_json(string $file, array $fallback = []): array {
    if (!file_exists($file)) return $fallback;
    $raw = file_get_contents($file);
    $data = json_decode($raw, true);
    return is_array($data) ? $data : $fallback;
}

function mdw_write_json(string $file, array $data): bool {
    $dir = dirname($file);
    if (!is_dir($dir)) mkdir($dir, 0755, true);
    return file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) !== false;
}

function mdw_slug(string $text): string {
    $text = strtolower(trim($text));
    $text = preg_replace('/[^a-z0-9]+/', '-', $text);
    return trim($text, '-') ?: 'item';
}

function mdw_flash(?string $message = null, string $type = 'success'): ?array {
    mdw_start_admin();
    if ($message !== null) {
        $_SESSION['mdw_flash'] = ['message' => $message, 'type' => $type];
        return null;
    }
    if (!empty($_SESSION['mdw_flash'])) {
        $flash = $_SESSION['mdw_flash'];
        unset($_SESSION['mdw_flash']);
        return $flash;
    }
    return null;
}


function mdw_normalize_update_exclusions(): void {
    global $mdw_update_exclusions;
    if (!is_array($mdw_update_exclusions ?? null)) $mdw_update_exclusions = [];
    $extra = [
        'devworks-admin/documents/saved/',
        'devworks-admin/documents/invoices/',
        'devworks-admin/documents/exports/',
        'devworks-admin/documents/archive/',
        'devworks-admin/backups/',
        'devworks-admin/logs/',
    ];
    foreach ($extra as $rule) {
        if (!in_array($rule, $mdw_update_exclusions, true)) $mdw_update_exclusions[] = $rule;
    }
}

function mdw_data_file(string $name): string {
    return __DIR__ . '/../data/' . $name;
}

function mdw_admin_dir(): string {
    return realpath(__DIR__ . '/..') ?: (__DIR__ . '/..');
}

function mdw_storage_path(string $key): string {
    global $mdw_documents_dir, $mdw_document_templates_dir, $mdw_document_saved_dir, $mdw_document_invoices_dir, $mdw_document_exports_dir, $mdw_document_archive_dir, $mdw_backups_dir, $mdw_logs_dir, $mdw_tmp_dir;
    $map = [
        'documents' => $mdw_documents_dir ?? (mdw_admin_dir() . '/documents'),
        'templates' => $mdw_document_templates_dir ?? (mdw_admin_dir() . '/documents/templates'),
        'saved' => $mdw_document_saved_dir ?? (mdw_admin_dir() . '/documents/saved'),
        'records' => mdw_admin_dir() . '/documents/saved/records',
        'invoices' => $mdw_document_invoices_dir ?? (mdw_admin_dir() . '/documents/invoices'),
        'exports' => $mdw_document_exports_dir ?? (mdw_admin_dir() . '/documents/exports'),
        'archive' => $mdw_document_archive_dir ?? (mdw_admin_dir() . '/documents/archive'),
        'backups' => $mdw_backups_dir ?? (mdw_admin_dir() . '/backups'),
        'logs' => $mdw_logs_dir ?? (mdw_admin_dir() . '/logs'),
        'tmp' => $mdw_tmp_dir ?? (mdw_admin_dir() . '/tmp'),
    ];
    return $map[$key] ?? mdw_admin_dir();
}

function mdw_documents_file(): string {
    global $mdw_documents_file;
    return $mdw_documents_file ?? (mdw_storage_path('saved') . '/documents.json');
}

function mdw_ensure_storage(): void {
    foreach (['documents','templates','saved','records','invoices','exports','archive','backups','logs','tmp'] as $key) {
        $dir = mdw_storage_path($key);
        if (!is_dir($dir)) @mkdir($dir, 0755, true);
        $keep = $dir . '/.htaccess';
        if (in_array($key, ['saved','records','invoices','exports','archive','backups','logs'], true) && !file_exists($keep)) {
            @file_put_contents($keep, "Options -Indexes\n");
        }
    }

    // Migrate old saved-documents list from /data/documents.json to /documents/saved/documents.json.
    $newFile = mdw_documents_file();
    $oldFile = mdw_data_file('documents.json');
    if (!file_exists($newFile) && file_exists($oldFile)) {
        $old = mdw_read_json($oldFile, []);
        $old = is_array($old) ? $old : [];
        mdw_write_json($newFile, $old);
        $recordsDir = mdw_storage_path('records');
        $invoiceDir = mdw_storage_path('invoices');
        if (!is_dir($recordsDir)) @mkdir($recordsDir, 0755, true);
        if (!is_dir($invoiceDir)) @mkdir($invoiceDir, 0755, true);
        foreach ($old as $doc) {
            if (empty($doc['id'])) continue;
            $id = mdw_slug((string)$doc['id']);
            @file_put_contents($recordsDir . '/' . $id . '.json', json_encode($doc, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
            if (($doc['template'] ?? '') === 'invoice') {
                $invoiceNumber = mdw_slug((string)($doc['source_reference'] ?? (($doc['fields']['invoice_number'] ?? $id))));
                @file_put_contents($invoiceDir . '/' . $invoiceNumber . '.json', json_encode($doc, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
            }
        }
    }

    // Keep template files in documents/templates/ so the folder is visibly active and easy to back up.
    mdw_sync_template_files();

    // If documents.json is missing but individual saved record files exist, rebuild the index.
    mdw_rebuild_documents_index_from_records();
}

function mdw_sync_template_files(): void {
    if (!function_exists('mdw_document_templates')) return;
    $templatesDir = mdw_storage_path('templates');
    if (!is_dir($templatesDir)) @mkdir($templatesDir, 0755, true);

    $templates = mdw_document_templates();
    $summary = [];
    foreach ($templates as $key => $template) {
        $safeKey = mdw_slug((string)$key);
        $record = [
            'key' => $key,
            'name' => $template['name'] ?? $key,
            'category' => $template['category'] ?? 'General',
            'description' => $template['description'] ?? '',
            'quick' => !empty($template['quick']),
            'fields' => $template['fields'] ?? [],
            'synced_at' => date('Y-m-d H:i:s'),
        ];
        $summary[$key] = [
            'file' => $safeKey . '.json',
            'name' => $record['name'],
            'category' => $record['category'],
            'description' => $record['description'],
            'fields' => array_keys($template['fields'] ?? []),
        ];
        @file_put_contents($templatesDir . '/' . $safeKey . '.json', json_encode($record, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
    }
    @file_put_contents($templatesDir . '/template-catalog.json', json_encode($summary, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}

function mdw_rebuild_documents_index_from_records(): void {
    $indexFile = mdw_documents_file();
    $current = mdw_read_json($indexFile, []);
    if (!empty($current)) return;

    $recordsDir = mdw_storage_path('records');
    if (!is_dir($recordsDir)) return;

    $documents = [];
    foreach (glob($recordsDir . '/*.json') ?: [] as $file) {
        $doc = mdw_read_json($file, []);
        if (!empty($doc['id'])) $documents[] = $doc;
    }
    if ($documents) {
        usort($documents, function($a, $b) {
            return strcmp((string)($a['created_at'] ?? ''), (string)($b['created_at'] ?? ''));
        });
        mdw_write_json($indexFile, $documents);
    }
}

function mdw_saved_documents(): array {
    mdw_normalize_update_exclusions();
mdw_ensure_storage();
    return mdw_read_json(mdw_documents_file(), []);
}


function mdw_document_record_path(string $id): string {
    return mdw_storage_path('records') . '/' . mdw_slug($id) . '.json';
}

function mdw_sync_document_record_files(array $documents): void {
    $recordsDir = mdw_storage_path('records');
    if (!is_dir($recordsDir)) return;
    $active = [];
    foreach ($documents as $doc) {
        if (!empty($doc['id'])) $active[mdw_slug((string)$doc['id']) . '.json'] = true;
    }
    foreach (glob($recordsDir . '/*.json') ?: [] as $file) {
        if (!isset($active[basename($file)])) @unlink($file);
    }
}

function mdw_delete_saved_document(string $id, ?string &$message = null): bool {
    mdw_normalize_update_exclusions();
    mdw_ensure_storage();
    $id = trim($id);
    if ($id === '') {
        $message = 'Missing document ID.';
        return false;
    }

    $documents = mdw_read_json(mdw_documents_file(), []);
    $deletedDoc = null;
    $remaining = [];
    foreach ($documents as $doc) {
        if (($doc['id'] ?? '') === $id) {
            $deletedDoc = $doc;
            continue;
        }
        $remaining[] = $doc;
    }

    $ok = mdw_write_json(mdw_documents_file(), $remaining);

    // Delete the physical editable source record.
    $recordFile = mdw_document_record_path($id);
    if (is_file($recordFile) && !@unlink($recordFile)) $ok = false;

    // Remove stale record files that are not in the saved document index.
    mdw_sync_document_record_files($remaining);

    // Delete related invoice record if this was an invoice.
    if (is_array($deletedDoc) && (($deletedDoc['template'] ?? '') === 'invoice')) {
        $invoiceRef = (string)($deletedDoc['source_reference'] ?? ($deletedDoc['fields']['invoice_number'] ?? $id));
        $invoiceFile = mdw_storage_path('invoices') . '/' . mdw_slug($invoiceRef) . '.json';
        if (is_file($invoiceFile) && !@unlink($invoiceFile)) $ok = false;
    }

    // Delete any generated export files tied to this document ID.
    $exportsDir = mdw_storage_path('exports');
    if (is_dir($exportsDir)) {
        foreach (glob($exportsDir . '/' . mdw_slug($id) . '.*') ?: [] as $file) {
            if (is_file($file) && !@unlink($file)) $ok = false;
        }
    }

    $message = $ok ? 'Document deleted.' : 'Document removed from list, but one or more physical files could not be deleted. Check file permissions.';
    return $ok;
}

function mdw_write_documents(array $documents): bool {
    mdw_normalize_update_exclusions();
    mdw_ensure_storage();
    $ok = mdw_write_json(mdw_documents_file(), $documents);

    $recordsDir = mdw_storage_path('records');
    $invoiceDir = mdw_storage_path('invoices');
    if (!is_dir($recordsDir)) @mkdir($recordsDir, 0755, true);
    if (!is_dir($invoiceDir)) @mkdir($invoiceDir, 0755, true);

    mdw_sync_document_record_files($documents);
    foreach ($documents as $doc) {
        if (empty($doc['id'])) continue;
        $id = mdw_slug((string)$doc['id']);
        @file_put_contents($recordsDir . '/' . $id . '.json', json_encode($doc, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
        if (($doc['template'] ?? '') === 'invoice') {
            $invoiceNumber = mdw_slug((string)($doc['source_reference'] ?? (($doc['fields']['invoice_number'] ?? $id))));
            @file_put_contents($invoiceDir . '/' . $invoiceNumber . '.json', json_encode($doc, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
        }
    }
    return $ok;
}

function mdw_safe_relative(string $path): string {
    $base = mdw_admin_dir();
    $real = realpath($path);
    if ($real && str_starts_with($real, $base)) return ltrim(str_replace('\\', '/', substr($real, strlen($base))), '/');
    return basename($path);
}

function mdw_zip_add_path(ZipArchive $zip, string $path, string $base): void {
    if (!file_exists($path)) return;
    if (is_file($path)) {
        $zip->addFile($path, ltrim(str_replace('\\', '/', substr($path, strlen($base))), '/'));
        return;
    }
    $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS));
    foreach ($it as $file) {
        if ($file->isFile()) {
            $full = $file->getPathname();
            $rel = ltrim(str_replace('\\', '/', substr($full, strlen($base))), '/');
            $zip->addFile($full, $rel);
        }
    }
}

function mdw_create_backup_zip(string &$message = ''): ?string {
    mdw_normalize_update_exclusions();
mdw_ensure_storage();
    if (!class_exists('ZipArchive')) {
        $message = 'ZipArchive is not available on this server.';
        return null;
    }
    $backupDir = mdw_storage_path('backups');
    if (!is_dir($backupDir) && !mkdir($backupDir, 0755, true)) {
        $message = 'Could not create backups folder.';
        return null;
    }
    $file = $backupDir . '/mdw-admin-backup-' . date('Ymd-His') . '.zip';
    $zip = new ZipArchive();
    if ($zip->open($file, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
        $message = 'Could not create backup ZIP.';
        return null;
    }
    $base = mdw_admin_dir() . '/';
    foreach (['data','documents','uploads'] as $dir) mdw_zip_add_path($zip, mdw_admin_dir() . '/' . $dir, $base);
    $zip->addFromString('backup-info.txt', "Murdock DevWorks Admin Backup\nCreated: " . date('Y-m-d H:i:s') . "\nVersion: " . MDW_ADMIN_VERSION . "\n\nThis backup includes data, documents, and uploads. It intentionally excludes config.php, updates, logs, tmp, and the Ecommerce Templates store.\n");
    $zip->close();
    $message = 'Backup created successfully.';
    return $file;
}

function mdw_settings(): array {
    $defaults = [
        'invoice_prefix' => 'MDW-INV',
        'invoice_year' => date('Y'),
        'next_invoice_number' => 1,
    ];
    $settings = mdw_read_json(mdw_data_file('settings.json'), []);
    return array_merge($defaults, is_array($settings) ? $settings : []);
}

function mdw_save_settings(array $settings): bool {
    return mdw_write_json(mdw_data_file('settings.json'), $settings);
}

function mdw_peek_next_invoice_number(): string {
    $settings = mdw_settings();
    $year = (string)($settings['invoice_year'] ?? date('Y'));
    if ($year !== date('Y')) {
        $year = date('Y');
        $settings['invoice_year'] = $year;
        $settings['next_invoice_number'] = 1;
    }
    $prefix = trim((string)($settings['invoice_prefix'] ?? 'MDW-INV')) ?: 'MDW-INV';
    $next = max(1, (int)($settings['next_invoice_number'] ?? 1));
    return $prefix . '-' . $year . '-' . str_pad((string)$next, 4, '0', STR_PAD_LEFT);
}

function mdw_claim_next_invoice_number(): string {
    $settings = mdw_settings();
    $year = (string)($settings['invoice_year'] ?? date('Y'));
    if ($year !== date('Y')) {
        $year = date('Y');
        $settings['invoice_year'] = $year;
        $settings['next_invoice_number'] = 1;
    }
    $prefix = trim((string)($settings['invoice_prefix'] ?? 'MDW-INV')) ?: 'MDW-INV';
    $next = max(1, (int)($settings['next_invoice_number'] ?? 1));
    $invoiceNumber = $prefix . '-' . $year . '-' . str_pad((string)$next, 4, '0', STR_PAD_LEFT);
    $settings['next_invoice_number'] = $next + 1;
    mdw_save_settings($settings);
    return $invoiceNumber;
}

function mdw_is_blank_invoice_number(string $value): bool {
    $value = trim($value);
    return $value === '' || stripos($value, 'auto') !== false || preg_match('/^MDW-\d{8}$/', $value);
}

function mdw_clients(): array {
    global $mdw_clients_file;
    $file = $mdw_clients_file ?? mdw_data_file('clients.json');
    return mdw_read_json($file, []);
}

function mdw_client_name(array $clients, string $clientId): string {
    foreach ($clients as $client) {
        if (($client['id'] ?? '') === $clientId) return (string)($client['business_name'] ?: ($client['client_name'] ?? ''));
    }
    return '';
}

function mdw_render_header(string $title): void {
    $flash = mdw_flash();
    $currentPage = basename($_SERVER['PHP_SELF'] ?? 'index.php');
    $isDocPage = str_starts_with($currentPage, 'document') || $currentPage === 'documents.php';
    $navClass = function(string $page) use ($currentPage): string {
        return $currentPage === $page ? ' class="active"' : '';
    };
    ?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title><?php echo mdw_h($title); ?> | MDW Admin</title>
  <link rel="stylesheet" href="assets/admin.css">
</head>
<body>
<header class="mdwa-mobilebar">
  <button class="mdwa-menu-toggle" type="button" aria-label="Open admin menu" aria-controls="mdwa-sidebar" aria-expanded="false" onclick="mdwToggleMenu()">☰</button>
  <div class="mdwa-mobile-title">
    <strong>Murdock DevWorks Admin</strong>
    <span><?php echo mdw_h($title); ?></span>
  </div>
  <a class="mdwa-view-site" href="../" target="_blank" rel="noopener">View Site</a>
</header>
<div class="mdwa-overlay" onclick="mdwCloseMenu()" aria-hidden="true"></div>
<div class="mdwa-layout">
<aside class="mdwa-sidebar" id="mdwa-sidebar">
  <div class="mdwa-brand">
    <img class="mdwa-logo-icon" src="../images/logo/murdock-devworks-icon.png" alt="Murdock DevWorks" onerror="this.style.display='none'">
    <div class="mdwa-brand-text">
      <strong class="mdwa-brand-name"><span>Murdock</span><span>DevWorks</span></strong>
      <span class="mdwa-brand-admin">Admin</span>
      <span class="mdwa-brand-page"><?php echo mdw_h($title); ?></span>
    </div>
  </div>

  <nav class="mdwa-nav" aria-label="Admin navigation">
    <a href="index.php"<?php echo $navClass('index.php'); ?>>Dashboard</a>
    <a href="clients.php"<?php echo $navClass('clients.php'); ?>>Clients</a>

    <details class="mdwa-nav-flyout" <?php echo $isDocPage ? 'open' : ''; ?>>
      <summary class="<?php echo $isDocPage ? 'active' : ''; ?>">Documents</summary>
      <div class="mdwa-flyout-panel">
        <a href="documents.php"<?php echo $navClass('documents.php'); ?>>Templates</a>
        <a href="document-edit.php"<?php echo $currentPage === 'document-edit.php' ? ' class="active"' : ''; ?>>New Document</a>
        <a href="documents.php#saved-documents">Saved Documents</a>
        <a href="invoices-export.php"<?php echo $navClass('invoices-export.php'); ?>>Invoice CSV</a>
      </div>
    </details>

    <a href="projects.php"<?php echo $navClass('projects.php'); ?>>Projects</a>
    <a href="downloads.php"<?php echo $navClass('downloads.php'); ?>>Downloads</a>
    <a href="backup.php"<?php echo $navClass('backup.php'); ?>>Backups</a>
    <a href="updater.php"<?php echo $navClass('updater.php'); ?>>Updater</a>
    <a href="../" target="_blank" rel="noopener">View Website</a>
    <a class="logout" href="logout.php">Logout</a>
  </nav>
</aside>
<section class="mdwa-content">
<main class="mdwa-shell">
<?php if ($flash): ?>
  <div class="alert <?php echo mdw_h($flash['type']); ?>"><?php echo mdw_h($flash['message']); ?></div>
<?php endif; ?>
    <?php
}

function mdw_render_footer(): void {
    ?>
</main>
<footer class="mdwa-footer">Murdock DevWorks Admin v<?php echo mdw_h(MDW_ADMIN_VERSION); ?> &mdash; separate from Ecommerce Templates</footer>
</section>
</div>
<script>
function mdwToggleMenu(){
  var body = document.body;
  var expanded = body.classList.toggle('mdwa-menu-open');
  var btn = document.querySelector('.mdwa-menu-toggle');
  if(btn){ btn.setAttribute('aria-expanded', expanded ? 'true' : 'false'); }
}
function mdwCloseMenu(){
  document.body.classList.remove('mdwa-menu-open');
  var btn = document.querySelector('.mdwa-menu-toggle');
  if(btn){ btn.setAttribute('aria-expanded','false'); }
}
document.addEventListener('keydown', function(e){ if(e.key === 'Escape') mdwCloseMenu(); });
document.querySelectorAll('.mdwa-nav a').forEach(function(link){
  link.addEventListener('click', function(){ if(window.innerWidth <= 900) mdwCloseMenu(); });
});
</script>
</body>
</html>
    <?php
}

function mdw_is_excluded(string $relativePath, array $exclusions): bool {
    $relativePath = ltrim(str_replace('\\', '/', $relativePath), '/');
    foreach ($exclusions as $rule) {
        $rule = ltrim(str_replace('\\', '/', $rule), '/');
        if (str_ends_with($rule, '/')) {
            if (str_starts_with($relativePath, $rule)) return true;
        } elseif ($relativePath === $rule) {
            return true;
        }
    }
    return false;
}

mdw_normalize_update_exclusions();
mdw_ensure_storage();
