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

/**
 * Shared PDF export progress (written by generator, polled by browser).
 */
class Pdf_progress
{
    public static function sanitize_token($token)
    {
        $token = preg_replace('/[^a-zA-Z0-9_\-]/', '', (string) $token);
        return $token;
    }

    public static function path($token)
    {
        $token = self::sanitize_token($token);
        if ($token === '') {
            return null;
        }
        $dir = APPPATH . 'cache';
        if (!is_dir($dir)) {
            @mkdir($dir, 0755, true);
        }
        return $dir . DIRECTORY_SEPARATOR . 'pdf_progress_' . $token . '.json';
    }

    public static function write($token, array $data)
    {
        $path = self::path($token);
        if ($path === null) {
            return false;
        }
        $data['updated_at'] = microtime(true);
        if (!isset($data['started_at'])) {
            $existing = self::read($token);
            $data['started_at'] = isset($existing['started_at'])
                ? $existing['started_at']
                : microtime(true);
        }
        $json = json_encode($data);
        return $json !== false && @file_put_contents($path, $json, LOCK_EX) !== false;
    }

    public static function read($token)
    {
        $path = self::path($token);
        if ($path === null || !is_file($path)) {
            return null;
        }
        $raw = @file_get_contents($path);
        if ($raw === false || $raw === '') {
            return null;
        }
        $data = json_decode($raw, true);
        return is_array($data) ? $data : null;
    }

    public static function clear($token)
    {
        $path = self::path($token);
        if ($path && is_file($path)) {
            @unlink($path);
        }
    }

    /**
     * Build progress payload with percent + ETA.
     *
     * @param string $token
     * @param string $status starting|rendering|merging|done|error
     * @param int    $current
     * @param int    $total
     * @param array  $extra
     * @return array
     */
    public static function snapshot($token, $status, $current, $total, array $extra = array())
    {
        $existing = self::read($token);
        $started = isset($existing['started_at'])
            ? (float) $existing['started_at']
            : (isset($extra['started_at']) ? (float) $extra['started_at'] : microtime(true));

        $current = max(0, (int) $current);
        $total = max(0, (int) $total);
        $percent = 0;
        if ($status === 'done') {
            $percent = 100;
        } elseif ($status === 'merging') {
            $percent = $total > 0 ? 95 : 90;
        } elseif ($total > 0) {
            // Leave headroom for merge phase
            $percent = min(94, (int) floor(($current / $total) * 94));
        }

        $elapsed = max(0.001, microtime(true) - $started);
        $eta_seconds = null;
        $finish_at = null;

        if ($status === 'done') {
            $eta_seconds = 0;
            $finish_at = time();
        } elseif ($status === 'merging') {
            // Rough merge estimate: ~0.02s per part, min 2s
            $eta_seconds = max(2, (int) ceil($total * 0.02));
            $finish_at = time() + $eta_seconds;
        } elseif ($current > 0 && $total > $current) {
            $per = $elapsed / $current;
            $eta_seconds = (int) ceil($per * ($total - $current) + max(2, $total * 0.02));
            $finish_at = time() + $eta_seconds;
        } elseif ($total > 0 && $current === 0) {
            // Before first chunk finishes — optimistic 0.7s/chunk + merge
            $eta_seconds = (int) ceil($total * 0.7 + max(2, $total * 0.02));
            $finish_at = time() + $eta_seconds;
        }

        $payload = array_merge(array(
            'token' => self::sanitize_token($token),
            'status' => $status,
            'current' => $current,
            'total' => $total,
            'percent' => $percent,
            'started_at' => $started,
            'elapsed_seconds' => (int) floor($elapsed),
            'eta_seconds' => $eta_seconds,
            'finish_at' => $finish_at,
            'message' => '',
        ), $extra);

        if ($payload['message'] === '') {
            if ($status === 'starting') {
                $payload['message'] = 'Starting PDF export…';
            } elseif ($status === 'rendering') {
                $payload['message'] = $total > 0
                    ? ('Rendering PDF part ' . $current . ' of ' . $total . '…')
                    : ('Rendering PDF part ' . $current . '…');
            } elseif ($status === 'merging') {
                $payload['message'] = 'Merging parts into one PDF…';
            } elseif ($status === 'done') {
                $payload['message'] = 'PDF ready — download starting…';
            } elseif ($status === 'error') {
                $payload['message'] = 'PDF export failed.';
            }
        }

        return $payload;
    }

    public static function update($token, $status, $current, $total, array $extra = array())
    {
        if (self::sanitize_token($token) === '') {
            return false;
        }
        return self::write($token, self::snapshot($token, $status, $current, $total, $extra));
    }
}
