<?php

namespace GriffinForms\Admin\Html\Elements;

class LogsTimeline extends \GriffinForms\Admin\Html\Format
{
    protected $item_type = '';
    protected $item_id = 0;
    protected $options = [];

    public function __construct($item_type = '', $item_id = 0, $options = [])
    {
        $this->item_type = sanitize_text_field((string) $item_type);
        $this->item_id = absint($item_id);
        $this->options = is_array($options) ? $options : [];
        $this->setAll();
    }

    public function html(): void
    {
        if (!empty($this->options['ajax'])) {
            $this->renderAjaxContainer();
            return;
        }

        $jobs = $this->getJobs();
        $logs = $this->getLogs();
        if (empty($logs) && empty($jobs)) {
            echo '<div class="gf-log-empty text-muted small">';
            echo esc_html($this->getEmptyMessage());
            echo '</div>';
            return;
        }

        echo '<div class="gf-log-timeline">';
        if (!empty($jobs)) {
            foreach ($jobs as $job_entry) {
                $this->renderJobEntry($job_entry);
            }
        }
        foreach ($logs as $log_entry) {
            $this->renderLogEntry($log_entry);
        }
        echo '</div>';
    }

    public function renderEntries(array $logs): string
    {
        if (empty($logs)) {
            return '';
        }

        ob_start();
        foreach ($logs as $log_entry) {
            $this->renderLogEntry($log_entry);
        }
        return ob_get_clean();
    }

    public function renderJobEntries(array $jobs): string
    {
        if (empty($jobs)) {
            return '';
        }

        ob_start();
        foreach ($jobs as $job_entry) {
            $this->renderJobEntry($job_entry);
        }
        return ob_get_clean();
    }

    protected function getLogs(): array
    {
        $log = \GriffinForms\Log::getInstance();
        if (!$log->isLoggingEnabledPublic()) {
            $this->options['empty_message'] = __(
                'Logging is currently disabled in Settings.',
                'griffinforms-form-builder'
            );
            return [];
        }

        $keyword = '';
        if (!empty($this->options['keyword'])) {
            $keyword = sanitize_text_field((string) $this->options['keyword']);
        }

        if ($keyword !== '') {
            $limit = isset($this->options['limit']) ? absint($this->options['limit']) : 5;
            $limit = max(1, $limit);
            $logs = $log->getByKeyword($limit, $keyword);
            return is_array($logs) ? $logs : [];
        }

        if ($this->item_type === '' || $this->item_id <= 0) {
            return [];
        }

        $logs = $log->getByItem($this->item_type, $this->item_id);
        if (!is_array($logs)) {
            return [];
        }

        $limit = isset($this->options['limit']) ? absint($this->options['limit']) : 0;
        if ($limit > 0) {
            return array_slice($logs, 0, $limit);
        }

        return $logs;
    }

    public function getJobs(): array
    {
        if (empty($this->options['include_jobs'])) {
            return [];
        }

        if ($this->item_type !== 'submission' && $this->item_type !== 'form') {
            return [];
        }

        $item_id = $this->item_id;
        if ($item_id <= 0) {
            return [];
        }

        $args = array(
            'statuses' => isset($this->options['job_statuses']) ? (array) $this->options['job_statuses'] : array('pending', 'processing'),
            'limit' => isset($this->options['job_limit']) ? absint($this->options['job_limit']) : 20,
            'order' => isset($this->options['job_order']) ? $this->options['job_order'] : 'asc',
            'time_field' => isset($this->options['job_time_field']) ? $this->options['job_time_field'] : 'available_at',
        );

        $jobs_sql = \GriffinForms\Admin\Sql\Jobs::getInstance();
        return $jobs_sql->getJobsForTimeline($this->item_type, $item_id, $args);
    }

    protected function getEmptyMessage(): string
    {
        if (!empty($this->options['empty_message'])) {
            return (string) $this->options['empty_message'];
        }

        return __('No logs recorded for this item yet.', 'griffinforms-form-builder');
    }

    protected function renderLogEntry($log_entry): void
    {
        if (!is_object($log_entry)) {
            return;
        }

        $type = $this->normalizeLogType($log_entry->log_type ?? '');
        $type_label = strtoupper($type);
        $message = isset($log_entry->message) ? sanitize_text_field($log_entry->message) : '';
        $category_raw = isset($log_entry->category) ? sanitize_text_field($log_entry->category) : '';
        $category = \GriffinForms\Log::getInstance()->normalizeCategoryPublic($category_raw);
        $date_time = $this->formatDateTime($log_entry->date_time ?? '');

        $node_class = 'gf-log-node gf-log-node-' . $type;
        $type_class = 'gf-log-type gf-log-type-' . $type;
        $icon_data = $this->getCategoryIconData($category, $type);
        $icon_url = $icon_data['url'];

        echo '<div class="gf-log-entry">';
        echo '<span class="' . esc_attr($node_class) . '"';
        if (!empty($icon_data['expected'])) {
            echo ' data-gf-icon-expected="' . esc_attr($icon_data['expected']) . '"';
        }
        if (!empty($icon_data['fallback'])) {
            echo ' data-gf-icon-fallback="1"';
        }
        echo '>';
        if ($icon_url !== '') {
            echo '<img src="' . esc_url($icon_url) . '" alt="" aria-hidden="true">';
        }
        echo '</span>';
        echo '<div class="gf-log-content">';
        echo '<div class="gf-log-meta">';
        echo '<span class="' . esc_attr($type_class) . '">' . esc_html($type_label) . '</span>';
        if ($category !== '') {
            echo '<span class="gf-log-category">' . esc_html($category) . '</span>';
        }
        echo '</div>';
        if ($date_time !== '') {
            echo '<div class="gf-log-time-row">';
            echo '<span class="gf-log-time">' . esc_html($date_time) . '</span>';
            echo '</div>';
        }
        if ($message !== '') {
            echo '<div class="gf-log-message">' . esc_html($message) . '</div>';
        }
        echo '</div>';
        echo '</div>';
    }

    protected function renderJobEntry($job_entry): void
    {
        if (!is_object($job_entry) && !is_array($job_entry)) {
            return;
        }

        $job = is_array($job_entry) ? (object) $job_entry : $job_entry;
        $job_type = isset($job->job_type) ? sanitize_text_field((string) $job->job_type) : '';
        $integration = isset($job->integration_id) ? sanitize_text_field((string) $job->integration_id) : '';
        $status = isset($job->status) ? sanitize_text_field((string) $job->status) : '';
        $priority = isset($job->priority) ? (int) $job->priority : null;
        $attempts = isset($job->attempts) ? (int) $job->attempts : null;
        $available_at = isset($job->available_at) ? $this->formatDateTime($job->available_at) : '';

        $label = $job_type !== '' ? $job_type : __('Scheduled job', 'griffinforms-form-builder');
        if ($integration !== '' && stripos($label, $integration) === false) {
            $label .= ' — ' . $integration;
        }

        $meta_parts = array();
        if ($status !== '') { $meta_parts[] = ucfirst($status); }
        if ($priority !== null) { $meta_parts[] = sprintf(__('Priority %d', 'griffinforms-form-builder'), $priority); }
        if ($attempts !== null) { $meta_parts[] = sprintf(__('Attempts %d', 'griffinforms-form-builder'), $attempts); }
        $meta_line = implode(' · ', $meta_parts);

        $icon_url = $this->getJobIconUrl();

        echo '<div class="gf-log-entry gf-log-entry-future">';
        echo '<span class="gf-log-node gf-log-node-future">';
        if ($icon_url !== '') {
            echo '<img src="' . esc_url($icon_url) . '" alt="" aria-hidden="true">';
        }
        echo '</span>';
        echo '<div class="gf-log-content">';
        echo '<div class="gf-log-meta">';
        echo '<span class="gf-log-type">' . esc_html($label) . '</span>';
        if ($meta_line !== '') {
            echo '<span class="gf-log-category">' . esc_html($meta_line) . '</span>';
        }
        echo '</div>';
        if ($available_at !== '') {
            echo '<div class="gf-log-time-row">';
            echo '<span class="gf-log-time">' . esc_html($available_at) . '</span>';
            echo '</div>';
        }
        echo '</div>';
        echo '</div>';
    }

    protected function normalizeLogType($log_type): string
    {
        $log_type = strtolower(sanitize_text_field((string) $log_type));
        $allowed = \GriffinForms\Log::getInstance()->getAllowedLogTypesPublic();
        $allowed = is_array($allowed) ? $allowed : array();
        return in_array($log_type, $allowed, true) ? $log_type : 'info';
    }

    protected function formatDateTime($raw): string
    {
        if (empty($raw)) {
            return '';
        }

        $timestamp = is_numeric($raw) ? (int) $raw : strtotime((string) $raw);
        if (!$timestamp) {
            return '';
        }

        $date_format = get_option('date_format');
        $time_format = get_option('time_format');
        return wp_date($date_format . ' ' . $time_format, $timestamp);
    }

    protected function getCategoryIconData(string $category, string $type): array
    {
        $normalized = $this->normalizeCategoryKey($category);
        if ($normalized === '') {
            $normalized = $type;
        }

        $expected = $normalized . '.svg';
        $base_path = $this->config->getRootPath() . 'admin/images/log-timeline-icons/';
        $base_url = $this->config->getRootUrl() . 'admin/images/log-timeline-icons/';

        $candidate_path = $base_path . $expected;
        if (file_exists($candidate_path)) {
            return array(
                'url' => $base_url . $expected,
                'expected' => $expected,
                'fallback' => false,
            );
        }

        $fallback_path = $base_path . 'fallback.svg';
        if (file_exists($fallback_path)) {
            return array(
                'url' => $base_url . 'fallback.svg',
                'expected' => $expected,
                'fallback' => true,
            );
        }

        return array(
            'url' => '',
            'expected' => $expected,
            'fallback' => true,
        );
    }

    protected function getJobIconUrl(): string
    {
        $base_path = $this->config->getRootPath() . 'admin/images/log-timeline-icons/';
        $base_url = $this->config->getRootUrl() . 'admin/images/log-timeline-icons/';
        $candidate_path = $base_path . 'hourglass.svg';
        if (file_exists($candidate_path)) {
            return $base_url . 'hourglass.svg';
        }

        $fallback_path = $base_path . 'fallback.svg';
        if (file_exists($fallback_path)) {
            return $base_url . 'fallback.svg';
        }

        return '';
    }

    protected function normalizeCategoryKey(string $category): string
    {
        $value = strtolower($category);
        $value = str_replace(array('/', '\\'), '-', $value);
        $value = str_replace(array('(', ')'), '', $value);
        $value = preg_replace('/\s+/', '-', $value);
        $value = preg_replace('/[^a-z0-9\-]/', '', $value);
        $value = preg_replace('/-+/', '-', $value);
        return trim($value, '-');
    }

    protected function renderAjaxContainer(): void
    {
        $per_page = isset($this->options['per_page']) ? absint($this->options['per_page']) : 20;
        $nonce = wp_create_nonce('griffinforms_logs_timeline');
        $include_jobs = !empty($this->options['include_jobs']) ? 1 : 0;

        echo '<div class="gf-log-timeline gf-log-timeline-ajax"';
        echo ' data-gf-log-ajax="1"';
        echo ' data-item-type="' . esc_attr($this->item_type) . '"';
        echo ' data-item-id="' . esc_attr($this->item_id) . '"';
        echo ' data-per-page="' . esc_attr($per_page) . '"';
        echo ' data-offset="0"';
        echo ' data-nonce="' . esc_attr($nonce) . '"';
        echo ' data-empty-message="' . esc_attr($this->getEmptyMessage()) . '"';
        echo ' data-include-jobs="' . esc_attr($include_jobs) . '"';
        echo '>';

        echo '<div class="gf-log-loading" data-gf-log-loading="1">';
        $this->renderPlaceholders($per_page);
        echo '</div>';

        $this->renderPlaceholderTemplate();

        echo '<div class="gf-log-sentinel" data-gf-log-sentinel="1"></div>';
        echo '</div>';
    }

    protected function renderPlaceholders(int $count): void
    {
        $count = max(1, $count);
        for ($i = 0; $i < $count; $i++) {
            echo $this->getPlaceholderEntryHtml();
        }
    }

    protected function renderPlaceholderTemplate(): void
    {
        echo '<template class="gf-log-placeholder-template" data-gf-log-placeholder="1">';
        echo $this->getPlaceholderEntryHtml();
        echo '</template>';
    }

    protected function getPlaceholderEntryHtml(): string
    {
        return '<div class="gf-log-entry gf-log-entry-placeholder">'
            . '<span class="gf-log-node gf-log-node-placeholder">'
            . '<span class="placeholder placeholder-glow"></span>'
            . '</span>'
            . '<div class="gf-log-content">'
            . '<div class="gf-log-meta placeholder-glow">'
            . '<span class="placeholder col-3"></span>'
            . '<span class="placeholder col-4"></span>'
            . '<span class="placeholder col-3"></span>'
            . '</div>'
            . '<div class="gf-log-message placeholder-glow">'
            . '<span class="placeholder col-8"></span>'
            . '</div>'
            . '</div>'
            . '</div>';
    }
}
