<?php
declare(strict_types=1);

require_once __DIR__ . '/subscriber_contracts_lib.php';
require_once __DIR__ . '/subscriber_contracts_pdf_lib.php';

function sc_bulk_export_output_format(string $format): string
{
    $format = strtolower(trim($format));
    return in_array($format, ['docx', 'pdf'], true) ? $format : 'docx';
}

function sc_bulk_export_owner(): string
{
    $identity = session_id() . '|' . (string)($_SESSION['admin_username'] ?? $_SESSION['admin_user'] ?? '');
    return hash('sha256', $identity);
}

function sc_bulk_export_job_relative(string $token): string
{
    if (!preg_match('/^[a-f0-9]{32}$/', $token)) {
        throw new RuntimeException('معرّف مهمة التصدير غير صالح.');
    }
    return 'generated/batches/jobs/' . $token . '.json';
}

function sc_bulk_export_save_job(array $job): void
{
    $relative = sc_bulk_export_job_relative((string)($job['token'] ?? ''));
    $path = sc_safe_storage_path($relative);
    $directory = dirname($path);
    if (!is_dir($directory) && !@mkdir($directory, 0770, true) && !is_dir($directory)) {
        throw new RuntimeException('تعذر تجهيز مجلد مهام التصدير.');
    }
    $temporary = $path . '.tmp-' . bin2hex(random_bytes(3));
    $json = json_encode($job, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
    if (file_put_contents($temporary, $json, LOCK_EX) === false || !@rename($temporary, $path)) {
        @unlink($temporary);
        throw new RuntimeException('تعذر حفظ حالة مهمة التصدير.');
    }
}

function sc_bulk_export_load_job(string $token): array
{
    $path = sc_safe_storage_path(sc_bulk_export_job_relative($token));
    if (!is_file($path)) throw new RuntimeException('مهمة التصدير غير موجودة أو انتهت صلاحيتها.');
    $job = json_decode((string)file_get_contents($path), true, 512, JSON_THROW_ON_ERROR);
    if (!is_array($job) || !hash_equals((string)($job['owner'] ?? ''), sc_bulk_export_owner())) {
        throw new RuntimeException('لا تملك صلاحية الوصول إلى مهمة التصدير.');
    }
    return $job;
}

function sc_bulk_export_requested_ids(mysqli $conn, string $scope, array $postedIds): array
{
    $ids = [];
    if ($scope === 'all') {
        $result = $conn->query('SELECT id FROM subscribers WHERE COALESCE(is_deleted,0)=0 ORDER BY id');
        while ($result && ($row = $result->fetch_assoc())) $ids[(int)$row['id']] = (int)$row['id'];
    } else {
        foreach ($postedIds as $subscriberId) {
            $subscriberId = (int)$subscriberId;
            if ($subscriberId > 0) $ids[$subscriberId] = $subscriberId;
        }
    }
    return array_values($ids);
}

function sc_bulk_export_subscribers(mysqli $conn, array $ids): array
{
    $byId = [];
    foreach (array_chunk($ids, 500) as $chunk) {
        $idSql = implode(',', array_map('intval', $chunk));
        if ($idSql === '') continue;
        $result = $conn->query(
            "SELECT s.*,COALESCE(sec.name,CAST(s.sector_id AS CHAR),'') sector_name
             FROM subscribers s
             LEFT JOIN sectors sec ON sec.id=s.sector_id
             WHERE s.id IN ($idSql) AND COALESCE(s.is_deleted,0)=0"
        );
        while ($result && ($subscriber = $result->fetch_assoc())) {
            $byId[(int)$subscriber['id']] = $subscriber;
        }
    }
    $ordered = [];
    foreach ($ids as $id) if (isset($byId[$id])) $ordered[] = $byId[$id];
    return $ordered;
}

function sc_bulk_export_start(mysqli $conn, array $input): array
{
    $templateId = max(0, (int)($input['template_id'] ?? 0));
    $scope = (string)($input['bulk_scope'] ?? 'selected');
    $legacyMode = (string)($input['export_mode'] ?? 'full');
    $dataFilter = sc_contract_export_filter((string)($input['data_filter'] ?? 'all'));
    $outputFormat = sc_bulk_export_output_format((string)($input['output_format'] ?? 'docx'));
    if (!in_array($legacyMode, ['full', 'changed'], true)) {
        throw new RuntimeException('نمط تصدير الصفحات غير صالح.');
    }

    $ids = sc_bulk_export_requested_ids($conn, $scope, (array)($input['subscriber_ids'] ?? []));
    if (!$ids) throw new RuntimeException('حدد مشتركاً واحداً على الأقل للتصدير.');

    $template = sc_resolve_contract_template($conn, $templateId);
    if (!$template) throw new RuntimeException('لا يوجد قالب Word معتمد للتصدير.');
    $templateId = (int)$template['id'];

    $templateFields = sc_template_field_rows($conn, $templateId);
    $catalog = sc_template_export_catalog((string)$template['file_path'], $templateFields);
    $pageNumbers = trim((string)($input['page_numbers'] ?? ''));
    $pageScope = $pageNumbers !== '' ? 'pages:' . $pageNumbers : (string)($input['page_scope'] ?? 'all');
    $selectedPages = sc_contract_selected_pages($catalog, $pageScope);
    $filterRequiredFields = sc_contract_filter_required_fields($catalog);
    $renderRequiredFields = sc_contract_render_required_fields($catalog, $selectedPages);

    $subscribers = sc_bulk_export_subscribers($conn, $ids);
    $partition = sc_filter_contract_export_rows(
        $subscribers,
        $filterRequiredFields,
        $dataFilter,
        sc_contract_field_catalog()
    );
    $eligible = (array)$partition['eligible'];
    $eligibleCount = (int)$partition['eligible_count'];
    $maxExportCount = sc_contract_export_max_count();
    if ($eligibleCount > $maxExportCount) {
        throw new RuntimeException('العدد النهائي للتصدير ' . $eligibleCount . ' ويتجاوز الحد الأعلى ' . $maxExportCount . ' مشترك.');
    }
    if ($eligibleCount < 1) {
        throw new RuntimeException('لا توجد عقود مطابقة لمرشح البيانات المحدد.');
    }

    $token = bin2hex(random_bytes(16));
    $batchRelative = 'generated/batches/' . date('Ymd-His') . '-' . substr($token, 0, 8);
    $batchDirectory = sc_safe_storage_path($batchRelative);
    if (!is_dir($batchDirectory) && !@mkdir($batchDirectory, 0770, true) && !is_dir($batchDirectory)) {
        throw new RuntimeException('تعذر تجهيز مجلد التصدير الجماعي.');
    }
    $zipRelative = $outputFormat === 'docx' ? $batchRelative . '/subscriber-contracts.zip' : '';
    $htmlRelative = $outputFormat === 'pdf' ? $batchRelative . '/subscriber-contracts.html' : '';
    $assetRelative = $outputFormat === 'pdf' ? $batchRelative . '/pdf-assets' : '';
    $pdfRelative = $outputFormat === 'pdf' ? $batchRelative . '/subscriber-contracts.pdf' : '';
    if ($outputFormat === 'docx') {
        $zip = new ZipArchive();
        if ($zip->open(sc_safe_storage_path($zipRelative), ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
            throw new RuntimeException('تعذر إنشاء ملف ZIP.');
        }
        $zip->close();
    } else {
        sc_pdf_chrome_path();
        sc_pdf_begin_document($htmlRelative);
    }

    $job = [
        'token' => $token,
        'owner' => sc_bulk_export_owner(),
        'status' => 'running',
        'created_at' => time(),
        'template_id' => $templateId,
        'template_path' => (string)$template['file_path'],
        'legacy_mode' => $legacyMode,
        'output_format' => $outputFormat,
        'data_filter' => $dataFilter,
        'page_scope' => $pageScope,
        'selected_pages' => $selectedPages,
        'filter_required_fields' => $filterRequiredFields,
        'render_required_fields' => $renderRequiredFields,
        'eligible_ids' => array_map(static fn(array $row): int => (int)$row['id'], $eligible),
        'omitted' => array_values((array)$partition['omitted']),
        'selected_count' => (int)$partition['selected_count'],
        'complete_count' => (int)$partition['complete_count'],
        'incomplete_count' => (int)$partition['incomplete_count'],
        'total' => $eligibleCount,
        'processed' => 0,
        'batch_relative' => $batchRelative,
        'zip_relative' => $zipRelative,
        'html_relative' => $htmlRelative,
        'asset_relative' => $assetRelative,
        'pdf_relative' => $pdfRelative,
        'rendered_pages' => 0,
        'download_token' => '',
        'error' => '',
    ];
    sc_bulk_export_save_job($job);
    return $job;
}

function sc_bulk_export_add_omitted_report(array $job): void
{
    if (($job['output_format'] ?? 'docx') !== 'docx') return;
    if (($job['data_filter'] ?? '') !== 'complete' || empty($job['omitted'])) return;
    $stream = fopen('php://temp', 'w+');
    if ($stream === false) return;
    fwrite($stream, "\xEF\xBB\xBF");
    fputcsv($stream, ['رقم المشترك', 'اسم المشترك', 'المعلومات الناقصة']);
    foreach ((array)$job['omitted'] as $row) {
        fputcsv($stream, [(int)($row['id'] ?? 0), (string)($row['name'] ?? ''), implode('، ', (array)($row['fields'] ?? []))]);
    }
    rewind($stream);
    $zip = new ZipArchive();
    if ($zip->open(sc_safe_storage_path((string)$job['zip_relative']), ZipArchive::CREATE) === true) {
        $zip->addFromString('العقود-غير-المكتملة.csv', (string)stream_get_contents($stream));
        $zip->close();
    }
    fclose($stream);
}

function sc_bulk_export_process_next(mysqli $conn, string $token): array
{
    $job = sc_bulk_export_load_job($token);
    if (($job['status'] ?? '') === 'complete') return $job;
    if (($job['status'] ?? '') === 'failed') {
        throw new RuntimeException((string)($job['error'] ?? 'فشلت مهمة التصدير.'));
    }

    $index = (int)($job['processed'] ?? 0);
    $total = (int)($job['total'] ?? 0);
    if ($index >= $total) {
        $job['status'] = 'complete';
        sc_bulk_export_save_job($job);
        return $job;
    }

    try {
        $subscriberId = (int)$job['eligible_ids'][$index];
        $stmt = $conn->prepare(
            "SELECT s.*,COALESCE(sec.name,CAST(s.sector_id AS CHAR),'') sector_name
             FROM subscribers s
             LEFT JOIN sectors sec ON sec.id=s.sector_id
             WHERE s.id=? AND COALESCE(s.is_deleted,0)=0 LIMIT 1"
        );
        if (!$stmt) throw new RuntimeException('تعذر قراءة بيانات المشترك.');
        $stmt->bind_param('i', $subscriberId);
        $stmt->execute();
        $subscriber = $stmt->get_result()->fetch_assoc();
        $stmt->close();
        if (!$subscriber) throw new RuntimeException('تعذر العثور على المشترك #' . $subscriberId . '.');

        $templateId = (int)$job['template_id'];
        $templateFields = sc_template_field_rows($conn, $templateId);
        $contractNumber = sc_contract_number($conn, $subscriberId);
        $title = 'عقد تقديم خدمة إنترنت';
        $map = sc_subscriber_placeholders($subscriber, [
            'company_name' => sc_setting_get($conn, 'company_name', 'الشركة'),
            'contract_number' => $contractNumber,
            'contract_date' => date('Y-m-d'),
            'contract_title' => $title,
            'contract_notes' => '',
        ]);
        $map = sc_apply_template_fields($map, $templateFields);
        $map['{{company_stamp}}'] = '';
        $map['{{company_signature}}'] = '';

        $docRelative = (string)$job['batch_relative'] . '/contract-' . $subscriberId . '-' . bin2hex(random_bytes(3)) . '.docx';
        $verificationValues = [];
        $renderRequiredFields = (array)($job['render_required_fields'] ?? $job['required_fields'] ?? []);
        foreach ($renderRequiredFields as $field) {
            $value = trim((string)($map['{{' . $field . '}}'] ?? ''));
            if ($value !== '') $verificationValues[] = $value;
        }
        $selectedPages = is_array($job['selected_pages']) ? array_map('intval', $job['selected_pages']) : null;
        sc_generate_docx(
            (string)$job['template_path'],
            $docRelative,
            $map,
            [],
            array_values(array_unique($verificationValues)),
            $selectedPages === null && (string)$job['legacy_mode'] === 'changed',
            $selectedPages
        );

        $name = 'عقد-' . preg_replace('/[^\p{L}\p{N}._-]+/u', '-', trim((string)($subscriber['full_name'] ?? $subscriberId))) . '-' . $contractNumber . '.docx';
        $outputFormat = sc_bulk_export_output_format((string)($job['output_format'] ?? 'docx'));
        if ($outputFormat === 'docx') {
            $zip = new ZipArchive();
            if ($zip->open(sc_safe_storage_path((string)$job['zip_relative']), ZipArchive::CREATE) !== true) {
                throw new RuntimeException('تعذر تحديث ملف ZIP.');
            }
            $zip->addFile(sc_safe_storage_path($docRelative), $name);
            $zip->close();
        } else {
            $job['rendered_pages'] = (int)($job['rendered_pages'] ?? 0) + sc_pdf_append_docx(
                $docRelative,
                (string)$job['html_relative'],
                (string)$job['asset_relative']
            );
        }

        $actor = (string)($_SESSION['admin_username'] ?? $_SESSION['admin_user'] ?? 'admin');
        $notes = '';
        $stmt = $conn->prepare('INSERT INTO subscriber_contracts(subscriber_id,template_id,contract_number,title,custom_notes,generated_path,generated_filename,created_by) VALUES(?,?,?,?,?,?,?,?)');
        if (!$stmt) throw new RuntimeException('تعذر تسجيل العقد المولد.');
        $stmt->bind_param('iissssss', $subscriberId, $templateId, $contractNumber, $title, $notes, $docRelative, $name, $actor);
        if (!$stmt->execute()) throw new RuntimeException('تعذر تسجيل العقد المولد.');
        $contractId = (int)$stmt->insert_id;
        $stmt->close();
        $pageNote = $selectedPages === null ? 'العقد الكامل' : 'الصفحة ' . $selectedPages[0];
        sc_event(
            $conn,
            $contractId,
            $subscriberId,
            $outputFormat === 'pdf' ? 'BULK_PDF' : 'BULK_GENERATE',
            ($outputFormat === 'pdf' ? 'إضافة إلى PDF الجماعي: ' : 'تصدير ') . $pageNote
        );

        $job['processed'] = $index + 1;
        if ((int)$job['processed'] >= $total) {
            if ($outputFormat === 'pdf') {
                sc_pdf_finalize_document((string)$job['html_relative'], (string)$job['pdf_relative']);
                $downloadPath = (string)$job['pdf_relative'];
                $downloadMime = 'application/pdf';
                $downloadName = 'subscriber-contracts.pdf';
            } else {
                sc_bulk_export_add_omitted_report($job);
                $downloadPath = (string)$job['zip_relative'];
                $downloadMime = 'application/zip';
                $downloadName = 'subscriber-contracts.zip';
            }
            $downloadToken = bin2hex(random_bytes(16));
            $_SESSION['sc_batch_downloads'][$downloadToken] = [
                'path' => $downloadPath,
                'mime' => $downloadMime,
                'filename' => $downloadName,
                'created' => time(),
            ];
            $job['download_token'] = $downloadToken;
            $job['status'] = 'complete';
        }
        sc_bulk_export_save_job($job);
        return $job;
    } catch (Throwable $error) {
        $job['status'] = 'failed';
        $job['error'] = $error->getMessage();
        sc_bulk_export_save_job($job);
        throw $error;
    }
}

function sc_bulk_export_public_state(array $job): array
{
    $total = max(0, (int)($job['total'] ?? 0));
    $processed = min($total, max(0, (int)($job['processed'] ?? 0)));
    return [
        'ok' => ($job['status'] ?? '') !== 'failed',
        'token' => (string)($job['token'] ?? ''),
        'status' => (string)($job['status'] ?? 'running'),
        'processed' => $processed,
        'total' => $total,
        'percent' => $total > 0 ? (int)floor(($processed / $total) * 100) : 0,
        'complete_count' => (int)($job['complete_count'] ?? 0),
        'incomplete_count' => (int)($job['incomplete_count'] ?? 0),
        'output_format' => sc_bulk_export_output_format((string)($job['output_format'] ?? 'docx')),
        'rendered_pages' => (int)($job['rendered_pages'] ?? 0),
        'download_url' => !empty($job['download_token'])
            ? 'subscriber_contracts.php?download_batch=' . rawurlencode((string)$job['download_token'])
            : '',
        'message' => (string)($job['error'] ?? ''),
    ];
}
