<?php
defined('BASEPATH') or exit('No direct script access allowed');

/**
 * Excel download helpers.
 * Uses MHTML (.xls) so header logos are embedded (CID) — Excel ignores remote/data-URI images in plain HTML.
 */
class Excel_export
{
    public function stream_html($html, $filename, $embedded_images = array())
    {
        if (stripos($filename, '.xls') === false) {
            $filename .= '.xls';
        }

        $CI =& get_instance();
        $CI->load->helper('report');

        // Already a full MHTML document
        if (stripos($html, 'MIME-Version:') === 0) {
            $this->send_headers($filename);
            echo $html;
            exit;
        }

        if (stripos($html, '<html') !== false) {
            if (preg_match('/<body[^>]*>(.*)<\/body>/is', $html, $bm)) {
                $html = $bm[1];
            }
        }

        $embedded = report_export_html_embed_local_images($html, $embedded_images);
        $html = report_export_build_mhtml(
            $embedded['html'],
            $embedded['images'],
            'table.report-table th, table.report-table td { mso-number-format:"\\@"; }'
        );

        $this->send_headers($filename);
        echo $html;
        exit;
    }

    protected function send_headers($filename)
    {
        $this->send_download_cookie();

        header('Content-Type: application/vnd.ms-excel; charset=UTF-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Cache-Control: max-age=0');
    }

    protected function send_download_cookie()
    {
        $CI =& get_instance();
        $token = (string) $CI->input->post('download_token');
        if ($token === '') {
            return;
        }
        $token = preg_replace('/[^a-zA-Z0-9_\-]/', '', $token);
        if ($token === '') {
            return;
        }
        setcookie('kohaDownloadToken', $token, time() + 120, '/');
    }

    /**
     * Stream a large table as CSV (Excel-friendly). Does not build the full file in memory.
     *
     * @param string   $filename
     * @param array    $columns label => key
     * @param callable $batch_callback function(int $offset, int $limit): array of row objects
     * @param int      $batch_size
     */
    public function stream_csv_batched($filename, array $columns, $batch_callback, $batch_size = 2000)
    {
        if (stripos($filename, '.csv') === false && stripos($filename, '.xls') === false) {
            $filename .= '.csv';
        }
        // Prefer .csv so Excel opens correctly for huge files
        $filename = preg_replace('/\.xls$/i', '.csv', $filename);
        if (stripos($filename, '.csv') === false) {
            $filename .= '.csv';
        }

        @ini_set('memory_limit', '512M');
        @set_time_limit(0);
        @ini_set('output_buffering', 'off');
        @ini_set('zlib.output_compression', '0');
        while (ob_get_level() > 0) {
            @ob_end_clean();
        }

        $this->send_download_cookie();
        header('Content-Type: text/csv; charset=UTF-8');
        header('Content-Disposition: attachment; filename="' . str_replace(array('"', "\r", "\n"), '', $filename) . '"');
        header('Cache-Control: max-age=0');
        header('Pragma: public');

        $out = fopen('php://output', 'w');
        // UTF-8 BOM for Excel
        fwrite($out, "\xEF\xBB\xBF");

        $headers = array();
        foreach ($columns as $label => $key) {
            $headers[] = is_int($label) ? $key : $label;
        }
        fputcsv($out, $headers);

        $offset = 0;
        $batch_size = max(100, (int) $batch_size);
        $CI =& get_instance();
        $CI->load->helper('report');

        while (true) {
            $rows = call_user_func($batch_callback, $offset, $batch_size);
            if (!is_array($rows) || empty($rows)) {
                break;
            }
            foreach ($rows as $row) {
                $line = array();
                foreach ($columns as $label => $key) {
                    if (is_int($label)) {
                        $key = $label;
                    }
                    $line[] = (string) report_cell($row, $key);
                }
                fputcsv($out, $line);
            }
            $offset += count($rows);
            if (function_exists('flush')) {
                @flush();
            }
            if (count($rows) < $batch_size) {
                break;
            }
            unset($rows);
            if (function_exists('gc_collect_cycles')) {
                gc_collect_cycles();
            }
        }

        fclose($out);
        exit;
    }

    /**
     * @param string $title
     * @param array  $columns label => key
     * @param array  $rows
     * @param string $filename
     */
    public function stream_table($title, $columns, $rows, $filename)
    {
        $CI =& get_instance();
        $CI->load->helper('report');

        // Use CID brand header (same as Word) — data-URI logos do not show in Excel
        $brand = report_export_brand_header_word();
        $html = $brand['html'];
        $images = $brand['images'];

        $html .= '<h3 style="text-align:center;margin:8px 0 12px;font-size:14pt;">' . htmlspecialchars($title) . '</h3>';
        $html .= '<table class="report-table" border="1" cellpadding="4" cellspacing="0" width="100%"><thead><tr>';
        foreach ($columns as $label => $key) {
            if (is_int($label)) {
                $label = $key;
            }
            $html .= '<th>' . htmlspecialchars($label) . '</th>';
        }
        $html .= '</tr></thead><tbody>';
        if (!empty($rows)) {
            foreach ($rows as $row) {
                $html .= '<tr>';
                foreach ($columns as $label => $key) {
                    if (is_int($label)) {
                        $label = $key;
                    }
                    $val = (string) report_cell($row, $key);
                    $align = report_export_cell_align($val, is_string($label) ? $label : '');
                    $html .= '<td style="text-align:' . $align . ';">' . htmlspecialchars($val) . '</td>';
                }
                $html .= '</tr>';
            }
        } else {
            $html .= '<tr><td colspan="' . max(1, count($columns)) . '" style="text-align:center;">No records</td></tr>';
        }
        $html .= '</tbody></table>';

        $this->stream_html($html, $filename, $images);
    }
}
