<?php
namespace GriffinForms\Admin\Actions;

use \GriffinForms\Traits\Singleton;

class Submission extends \GriffinForms\Admin\Actions\Format
{
    use Singleton;

    /**
     * Register WordPress hooks for submission lifecycle events.
     */
    public function register()
    {
        add_action('griffinforms_files_after_promote', [$this, 'checkStorageThresholdAfterPromotion'], 10, 1);
        add_action('griffinforms/files/after_promote', [$this, 'checkStorageThresholdAfterPromotion'], 10, 1);
    }

    /**
     * Check and trigger storage threshold alerts after a file promotion.
     *
     * @param array|int $file The promoted file context or file ID.
     */
    public function checkStorageThresholdAfterPromotion($file)
    {
        // Normalize input: either array context or integer file ID
        $context  = is_array($file) ? $file : array();
        $file_id  = is_array($file) ? (int) ($file['id'] ?? 0) : (int) $file;

        // Ensure settings object is available
        if (empty($this->settings) || !method_exists($this->settings, 'getOption')) {
            return;
        }

        $threshold_mb = $this->getThresholdMb();
        if ($threshold_mb <= 0) {
            return;
        }

        $attached_bytes  = $this->getAttachedBytes();
        $threshold_bytes = (int) round($threshold_mb * 1024 * 1024);

        $this->logStorageCheck($file_id, $attached_bytes, $threshold_bytes);

        if ($attached_bytes >= $threshold_bytes) {
            if (!$this->isAlertAlreadySent($threshold_mb)) {
                $tokens = $this->buildEmailTokens($attached_bytes, $threshold_bytes);
                $this->sendStorageAlert($file_id, $tokens, $threshold_mb, $attached_bytes);
            }
            do_action('griffinforms_files_storage_threshold_exceeded', (int) $attached_bytes, (int) $threshold_bytes);
        } else {
            $this->resetAlertStateIfNeeded($file_id);
            do_action('griffinforms_files_storage_threshold_ok', (int) $attached_bytes, (int) $threshold_bytes);
        }
    }

    /**
     * Get configured threshold in MB (float). Returns 0 when disabled/invalid.
     */
    protected function getThresholdMb(): float
    {
        $threshold_mb = $this->settings->getOption('files_storage_alert_threshold_mb', 0);
        return is_numeric($threshold_mb) ? (float) $threshold_mb : 0.0;
    }

    /**
     * Get total attached bytes from SQL layer.
     */
    protected function getAttachedBytes(): int
    {
        return (int) $this->sql->getTotalAttachedBytes();
    }

    /**
     * Log a lightweight storage check line (if logger available).
     */
    protected function logStorageCheck(int $file_id, int $attached_bytes, int $threshold_bytes): void
    {
        if (!empty($this->log)) {
            $this->log->add(
                'info',
                'file',
                (int) $file_id,
                get_current_user_id(),
                '[Actions\\Submission] Storage check after promotion — total=' . (int) $attached_bytes . 'B, threshold=' . (int) $threshold_bytes . 'B',
                'storage'
            );
        }
    }

    /**
     * Determine if an alert has already been sent for the current crossing/threshold.
     */
    protected function isAlertAlreadySent(float $threshold_mb): bool
    {
        $state = $this->getAlertState();
        $is_over = isset($state['is_over']) ? (bool) $state['is_over'] : false;
        $last_threshold_mb = isset($state['last_threshold_mb']) ? (float) $state['last_threshold_mb'] : 0.0;
        return $is_over && ($last_threshold_mb === (float) $threshold_mb);
    }

    /**
     * Build template tokens for the storage alert email.
     */
    protected function buildEmailTokens(int $attached_bytes, int $threshold_bytes): array
    {
        if (class_exists('GriffinForms\\Admin\\Units') && method_exists(\GriffinForms\Admin\Units::getInstance(), 'formatBytes')) {
            $used_h      = \GriffinForms\Admin\Units::getInstance()->formatBytes((int) $attached_bytes);
            $threshold_h = \GriffinForms\Admin\Units::getInstance()->formatBytes((int) $threshold_bytes);
        } else {
            $used_h      = sprintf(__('%.2f MB', 'griffinforms-form-builder'), ((int) $attached_bytes) / 1048576);
            $threshold_h = sprintf(__('%.2f MB', 'griffinforms-form-builder'), ((int) $threshold_bytes) / 1048576);
        }

        $settings_url    = admin_url('admin.php?page=griffinforms-settings&tab=filestorage');
        $submissions_url = admin_url('admin.php?page=griffinforms-submissions');

        return array(
            'total_used_h'     => $used_h,
            'threshold_h'      => $threshold_h,
            'total_used_bytes' => (int) $attached_bytes,
            'threshold_bytes'  => (int) $threshold_bytes,
            'settings_url'     => esc_url($settings_url),
            'submissions_url'  => esc_url($submissions_url),
            'site_name'        => wp_specialchars_decode(get_bloginfo('name'), ENT_QUOTES),
            'site_url'         => home_url('/'),
        );
    }

    /**
     * Send storage alert email using MailHandler (or ServerMail fallback) and persist alert state.
     */
    protected function sendStorageAlert(int $file_id, array $tokens, float $threshold_mb, int $attached_bytes): void
    {
        $sent = false;

        $template = new \GriffinForms\Includes\MailTemplates\StorageThresholdExceeded(array(
            'site_name'        => isset($tokens['site_name']) ? $tokens['site_name'] : wp_specialchars_decode(get_bloginfo('name'), ENT_QUOTES),
            'site_url'         => isset($tokens['site_url']) ? $tokens['site_url'] : home_url('/'),
            'current_bytes'    => (int) $attached_bytes,
            'threshold_mb'     => (float) $threshold_mb,
            // Optional humanized + links if used by the template
            'total_used_h'     => isset($tokens['total_used_h']) ? $tokens['total_used_h'] : null,
            'threshold_h'      => isset($tokens['threshold_h']) ? $tokens['threshold_h'] : null,
            'settings_url'     => isset($tokens['settings_url']) ? $tokens['settings_url'] : null,
            'submissions_url'  => isset($tokens['submissions_url']) ? $tokens['submissions_url'] : null,
        ));

        $subject = $template->getSubject();
        $content = $template->getContent();

        $admin_email = get_option('admin_email');

        $mailer = new \GriffinForms\Includes\Mailer\MailHandler();
        $mailer->setLogContext('file', (int) $file_id);
        $mailer->setSubmissionId(0);
        $mailer->setEmailSubject($subject);
        $mailer->setEmailContent($content);

        if (is_email($admin_email)) {
            $mailer->setEmailRecipient($admin_email);
            $sent = $mailer->sendMail();
        } else {
            $sent = false;
            if (!empty($this->log)) {
                $this->log->add(
                    'error',
                    'file',
                    (int) $file_id,
                    get_current_user_id(),
                    '[Actions\\Submission] Invalid admin email; storage alert not sent',
                    'storage'
                );
            }
        }

        $this->updateAlertStatePostSend($threshold_mb, $attached_bytes);

        if (!empty($this->log)) {
            $this->log->add(
                $sent ? 'success' : 'error',
                'file',
                (int) $file_id,
                get_current_user_id(),
                $sent ? '[Actions\\Submission] Storage alert email sent' : '[Actions\\Submission] Failed to send storage alert email',
                'storage'
            );
        }
    }

    /**
     * Persist alert state after sending so we don't spam multiple alerts.
     */
    protected function updateAlertStatePostSend(float $threshold_mb, int $attached_bytes): void
    {
        $state = $this->getAlertState();
        $state['is_over']           = true;
        $state['last_alert_time']   = time();
        $state['last_alert_bytes']  = (int) $attached_bytes;
        $state['last_threshold_mb'] = (float) $threshold_mb;
        $this->updateAlertState($state);
    }

    /**
     * Reset alert state when usage drops below threshold so next crossing alerts again.
     */
    protected function resetAlertStateIfNeeded(int $file_id): void
    {
        $state = $this->getAlertState();
        if (!empty($state['is_over'])) {
            $state['is_over'] = false;
            $this->updateAlertState($state);

            if (!empty($this->log)) {
                $this->log->add(
                    'info',
                    'file',
                    (int) $file_id,
                    get_current_user_id(),
                    '[Actions\\Submission] Storage threshold back under limit — re-armed alert',
                    'storage'
                );
            }
        }
    }

    /**
     * Retrieve alert state using native Settings storage only.
     *
     * @return array
     */
    protected function getAlertState(): array
    {
        if (!empty($this->settings) && method_exists($this->settings, 'getOption')) {
            $state = $this->settings->getOption('files_storage_alert_state');
            return is_array($state) ? $state : array();
        }
        return array();
    }

    /**
     * Update alert state using native Settings storage only.
     *
     * @param array $state
     * @return void
     */
    protected function updateAlertState(array $state): void
    {
        if (!is_array($state)) {
            $state = array();
        }
        $has_settings = !empty($this->settings);
        $has_setter   = $has_settings && method_exists($this->settings, 'updateOption');

        if ($has_setter) {
            $this->settings->updateOption('files_storage_alert_state', $state, 'system');
            // Read back immediately to verify persistence
            $verify = $this->settings->getOption('files_storage_alert_state');
        }
    }
}
