<?php

namespace Formatrica\Admin;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use Formatrica\Forms\Form_Repository;
use Formatrica\Submission\Submission_Repository;

final class Export_Controller
{
    private Form_Repository $forms;
    private Submission_Repository $submissions;

    public function __construct(?Form_Repository $forms = null, ?Submission_Repository $submissions = null)
    {
        $this->forms = $forms ?? new Form_Repository();
        $this->submissions = $submissions ?? new Submission_Repository();
    }

    public function handle(): void
    {
        if (!current_user_can('manage_options')) {
            wp_die(esc_html__('You are not allowed to export submissions.', 'formatrica'), '', ['response' => 403]);
        }

        $form_id = isset($_GET['form_id']) ? (int) $_GET['form_id'] : 0;
        $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';

        if ($form_id <= 0 || !wp_verify_nonce($nonce, 'formatrica_export_' . $form_id)) {
            wp_die(esc_html__('Invalid export request.', 'formatrica'), '', ['response' => 400]);
        }

        $form = $this->forms->get($form_id);

        if (null === $form) {
            wp_die(esc_html__('Form not found.', 'formatrica'), '', ['response' => 404]);
        }

        $submissions = $this->submissions->all_for_form($form_id);

        $csv_filename = sprintf('form-%d-submissions.csv', $form_id);
        $zip_filename = sprintf('form-%d-submissions.zip', $form_id);

        $csv_path = wp_tempnam('fmt_csv_' . $form_id);
        if (false === $csv_path || !($handle = fopen($csv_path, 'w'))) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- writing temp CSV
            wp_die(esc_html__('Unable to create export file.', 'formatrica'), '', ['response' => 500]);
        }

        // Add UTF-8 BOM for Excel compatibility.
        fwrite($handle, "\xEF\xBB\xBF"); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite

        $field_map = $this->extract_field_map($form);

        $header = array_merge(
            [
                __('ID', 'formatrica'),
                __('UUID', 'formatrica'),
                __('Submitted at', 'formatrica'),
                __('Status', 'formatrica'),
                __('IP address', 'formatrica'),
            ],
            array_values($field_map)
        );

        fputcsv($handle, $header); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite

        foreach ($submissions as $entry) {
            $row = [
                $entry['id'],
                $entry['uuid'],
                $entry['created_at'],
                $entry['status'],
                $entry['ip_address'],
            ];

            foreach ($field_map as $name => $label) {
                $value = $entry['data'][$name] ?? '';
                if (is_array($value)) {
                    $value = implode(', ', array_map('strval', $value));
                } elseif (is_bool($value)) {
                    $value = $value ? __('Yes', 'formatrica') : __('No', 'formatrica');
                }
                $row[] = $this->guard_cell($value);
            }

            fputcsv($handle, $row); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
        }

        fclose($handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose

        if (class_exists('ZipArchive')) {
            $zip_path = wp_tempnam('fmt_zip_' . $form_id);
            if (false === $zip_path) {
                $zip_path = $csv_path . '.zip';
            }

            $archive = new \ZipArchive();
            if ($archive->open($zip_path, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === true) {
                $archive->addFile($csv_path, $csv_filename);
                $archive->close();

                $this->stream_file($zip_path, 'application/zip', $zip_filename);

                // Clean up temporary files
                wp_delete_file($csv_path);
                wp_delete_file($zip_path);
                exit;
            }
        }

        // Fallback to raw CSV.
        $this->stream_file($csv_path, 'text/csv; charset=utf-8', $csv_filename);

        // Clean up temporary file
        wp_delete_file($csv_path);
        exit;
    }

    /**
     * Formula-injection guard: a submitter value beginning with = + - @ (or
     * tab/CR) gets an apostrophe prefix so spreadsheet apps treat the cell as
     * text rather than executing it. Mirrors Submission_Export_Controller.
     *
     * @param mixed $value
     */
    private function guard_cell($value): string
    {
        $value = is_scalar($value) ? (string) $value : '';
        if ($value !== '' && preg_match('/^[=+\-@\t\r]/', $value)) {
            return "'" . $value;
        }
        return $value;
    }

    /**
     * @param array<string, mixed> $form
     * @return array<string, string>
     */
    private function extract_field_map(array $form): array
    {
        $fields = $form['config']['fields'] ?? [];

        $map = [];

        foreach ($fields as $field) {
            if (!isset($field['name']) || !is_string($field['name'])) {
                continue;
            }

            $type = $field['type'] ?? '';
            if (in_array($type, ['button', 'custom_text'], true)) {
                continue;
            }

            $label = isset($field['label']) ? (string) $field['label'] : $field['name'];
            $map[$field['name']] = wp_strip_all_tags($label);
        }

        return $map;
    }

    private function stream_file(string $path, string $mime, string $filename): void
    {
        if (!file_exists($path)) {
            wp_die(esc_html__('Export file missing.', 'formatrica'), '', ['response' => 500]);
        }

        nocache_headers();
        header('Content-Type: ' . $mime);
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Length: ' . filesize($path)); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_filesize

        readfile($path); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- streaming temp file to client
    }
}
