<?php

namespace GriffinForms\Includes\Privacy;

use GriffinForms\Traits\MaybeDecode;

class PersonalData
{
    use MaybeDecode;

    protected $config;

    public function __construct()
    {
        $this->config = \GriffinForms\Config::getInstance();
        add_filter('wp_privacy_personal_data_exporters', [$this, 'registerExporters']);
        add_filter('wp_privacy_personal_data_erasers', [$this, 'registerErasers']);
    }

    public function registerExporters(array $exporters): array
    {
        $exporters['griffinforms-submissions'] = [
            'exporter_friendly_name' => __('GriffinForms Submissions', 'griffinforms-form-builder'),
            'callback' => [$this, 'exportSubmissionData'],
        ];

        return $exporters;
    }

    public function registerErasers(array $erasers): array
    {
        $erasers['griffinforms-submissions'] = [
            'eraser_friendly_name' => __('GriffinForms Submissions', 'griffinforms-form-builder'),
            'callback' => [$this, 'eraseSubmissionData'],
        ];

        return $erasers;
    }

    public function exportSubmissionData(string $email, int $page = 1): array
    {
        $page = max(1, $page);
        $limit = 20;
        $offset = ($page - 1) * $limit;

        $user = $email !== '' ? get_user_by('email', $email) : null;
        $user_id = $user ? (int) $user->ID : 0;

        $conditions = [];
        $params = [];
        if ($user_id > 0) {
            $conditions[] = 'author = %d';
            $params[] = $user_id;
        }
        if ($email !== '') {
            global $wpdb;
            $conditions[] = 'submission LIKE %s';
            $params[] = '%' . $wpdb->esc_like($email) . '%';
        }

        if (empty($conditions)) {
            return ['data' => [], 'done' => true];
        }

        global $wpdb;
        $table = $this->config->getTable('submission');
        $where_sql = implode(' OR ', $conditions);
        $query = $wpdb->prepare(
            "SELECT id, form_id, status, submission, submission_meta, submission_ip, submission_user_agent, created, payment_meta, layout_snapshot_id, form_layout_version, form_layout_hash FROM %i WHERE {$where_sql} ORDER BY id ASC LIMIT %d OFFSET %d",
            array_merge([$table], $params, [$limit, $offset])
        );
        $rows = $wpdb->get_results($query, ARRAY_A);

        $data = [];
        $log = class_exists('\\GriffinForms\\Log') ? \GriffinForms\Log::getInstance() : null;
        $actor_id = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        foreach ($rows as $row) {
            $submission_id = (int) ($row['id'] ?? 0);
            if ($submission_id <= 0) {
                continue;
            }

            $submission_payload = $this->maybeDecode($row['submission'] ?? '');
            $meta_payload = $this->maybeDecode($row['submission_meta'] ?? '');
            $payment_meta = $this->maybeDecode($row['payment_meta'] ?? '');

            $compliance = [];
            $profile = '';
            if (is_array($meta_payload) && isset($meta_payload['compliance']) && is_array($meta_payload['compliance'])) {
                $compliance = $meta_payload['compliance'];
                $profile = strtolower((string) ($compliance['profile'] ?? ''));
            }

            $ip = (string) ($row['submission_ip'] ?? '');
            $ua = (string) ($row['submission_user_agent'] ?? '');
            if ($profile === 'gdpr') {
                if ($ip !== '') {
                    $ip = $this->maskIp($ip);
                }
                if ($ua !== '') {
                    $ua = $this->summarizeUserAgent($ua);
                }
            }

            $files = $this->getFilesForSubmission($submission_id);

            $data[] = [
                'group_id' => 'griffinforms-submission-' . $submission_id,
                'group_label' => __('GriffinForms Submission', 'griffinforms-form-builder'),
                'item_id' => (string) $submission_id,
                'data' => array_filter([
                    ['name' => __('Submission ID', 'griffinforms-form-builder'), 'value' => (string) $submission_id],
                    ['name' => __('Form ID', 'griffinforms-form-builder'), 'value' => (string) ($row['form_id'] ?? '')],
                    ['name' => __('Status', 'griffinforms-form-builder'), 'value' => ((int) ($row['status'] ?? 0) === 1) ? __('Complete', 'griffinforms-form-builder') : __('Incomplete', 'griffinforms-form-builder')],
                    ['name' => __('Submitted at', 'griffinforms-form-builder'), 'value' => (string) ($row['created'] ?? '')],
                    ['name' => __('IP address', 'griffinforms-form-builder'), 'value' => $ip],
                    ['name' => __('User agent', 'griffinforms-form-builder'), 'value' => $ua],
                    ['name' => __('Compliance profile', 'griffinforms-form-builder'), 'value' => (string) ($profile !== '' ? $profile : '')],
                    ['name' => __('Compliance flags', 'griffinforms-form-builder'), 'value' => !empty($compliance['flags']) ? wp_json_encode($compliance['flags']) : ''],
                    ['name' => __('Submission data', 'griffinforms-form-builder'), 'value' => is_array($submission_payload) ? wp_json_encode($submission_payload) : ''],
                    ['name' => __('Submission meta', 'griffinforms-form-builder'), 'value' => is_array($meta_payload) ? wp_json_encode($meta_payload) : ''],
                    ['name' => __('Payment metadata', 'griffinforms-form-builder'), 'value' => is_array($payment_meta) ? wp_json_encode($this->redactPaymentMeta($payment_meta)) : ''],
                    ['name' => __('Layout snapshot ID', 'griffinforms-form-builder'), 'value' => (string) ($row['layout_snapshot_id'] ?? '')],
                    ['name' => __('Layout version', 'griffinforms-form-builder'), 'value' => (string) ($row['form_layout_version'] ?? '')],
                    ['name' => __('Layout hash', 'griffinforms-form-builder'), 'value' => (string) ($row['form_layout_hash'] ?? '')],
                    ['name' => __('Files', 'griffinforms-form-builder'), 'value' => !empty($files) ? wp_json_encode($files) : ''],
                ], function ($entry) {
                    return isset($entry['value']) && $entry['value'] !== '';
                })
            ];

            if (!empty($log)) {
                $log->add(
                    'info',
                    'submission',
                    $submission_id,
                    $actor_id,
                    sprintf('Personal data exported via WP tools (email=%s).', $email),
                    'privacy'
                );
            }
        }

        $done = empty($rows) || count($rows) < $limit;

        return [
            'data' => $data,
            'done' => $done,
            'messages' => empty($data) ? [__('No GriffinForms data matched this email address.', 'griffinforms-form-builder')] : [],
        ];
    }

    public function eraseSubmissionData(string $email, int $page = 1): array
    {
        $page = max(1, $page);
        $limit = 20;
        $offset = ($page - 1) * $limit;

        $user = $email !== '' ? get_user_by('email', $email) : null;
        $user_id = $user ? (int) $user->ID : 0;

        $conditions = [];
        $params = [];
        if ($user_id > 0) {
            $conditions[] = 'author = %d';
            $params[] = $user_id;
        }
        if ($email !== '') {
            global $wpdb;
            $conditions[] = 'submission LIKE %s';
            $params[] = '%' . $wpdb->esc_like($email) . '%';
        }

        if (empty($conditions)) {
            return [
                'items_removed' => false,
                'items_retained' => false,
                'messages' => [__('No GriffinForms data matched this email address.', 'griffinforms-form-builder')],
                'done' => true,
            ];
        }

        global $wpdb;
        $table = $this->config->getTable('submission');
        $where_sql = implode(' OR ', $conditions);
        $query = $wpdb->prepare(
            "SELECT id, submission, submission_meta, payment_meta FROM %i WHERE {$where_sql} ORDER BY id ASC LIMIT %d OFFSET %d",
            array_merge([$table], $params, [$limit, $offset])
        );
        $rows = $wpdb->get_results($query, ARRAY_A);

        $items_removed = false;
        $items_retained = false;
        $messages = [];
        $log = class_exists('\\GriffinForms\\Log') ? \GriffinForms\Log::getInstance() : null;
        $actor_id = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;

        foreach ($rows as $row) {
            $submission_id = (int) ($row['id'] ?? 0);
            if ($submission_id <= 0) {
                continue;
            }

            $submission_payload = $this->maybeDecode($row['submission'] ?? '');
            $meta_payload = $this->maybeDecode($row['submission_meta'] ?? '');
            $payment_meta = $this->maybeDecode($row['payment_meta'] ?? '');
            $anonymized_submission = $this->anonymizeSubmissionPayload($submission_payload);
            $redacted_payment = is_array($payment_meta) ? $this->redactPaymentMeta($payment_meta) : [];
            $update = $wpdb->update(
                $table,
                [
                    'submission' => wp_json_encode($anonymized_submission),
                    'submission_ip' => null,
                    'submission_user_agent' => null,
                    'payment_meta' => wp_json_encode($redacted_payment),
                ],
                ['id' => $submission_id],
                ['%s', '%s', '%s', '%s'],
                ['%d']
            );

            if ($update === false) {
                $items_retained = true;
                $messages[] = sprintf(__('Failed to anonymize submission %d.', 'griffinforms-form-builder'), $submission_id);
                if (!empty($log)) {
                    $log->add(
                        'error',
                        'submission',
                        $submission_id,
                        $actor_id,
                        sprintf('Personal data erase failed for submission %d (email=%s).', $submission_id, $email),
                        'privacy'
                    );
                }
            } else {
                $items_removed = true;
                if (!empty($log)) {
                    $log->add(
                        'info',
                        'submission',
                        $submission_id,
                        $actor_id,
                        sprintf('Personal data erased via WP tools (email=%s).', $email),
                        'privacy'
                    );
                }
            }

            $files_removed = $this->deleteFilesForSubmission($submission_id);
            if ($files_removed === false) {
                $items_retained = true;
                $messages[] = sprintf(__('Failed to remove one or more files for submission %d.', 'griffinforms-form-builder'), $submission_id);
                if (!empty($log)) {
                    $log->add(
                        'warning',
                        'submission',
                        $submission_id,
                        $actor_id,
                        sprintf('Personal data erase partial: file removal failed for submission %d (email=%s).', $submission_id, $email),
                        'privacy'
                    );
                }
            }
        }

        $done = empty($rows) || count($rows) < $limit;
        if (empty($rows)) {
            $messages[] = __('No GriffinForms data matched this email address.', 'griffinforms-form-builder');
        } elseif ($items_removed && !$items_retained) {
            $messages[] = __('GriffinForms data erased successfully.', 'griffinforms-form-builder');
        }

        return [
            'items_removed' => $items_removed,
            'items_retained' => $items_retained,
            'messages' => $messages,
            'done' => $done,
        ];
    }

    private function getFilesForSubmission(int $submission_id): array
    {
        global $wpdb;
        $table = $this->config->getTable('files');
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT token, path, url, mime, size_bytes, status FROM %i WHERE submission_id = %d",
                $table,
                $submission_id
            ),
            ARRAY_A
        );
        if (!is_array($rows)) {
            return [];
        }
        $files = [];
        foreach ($rows as $row) {
            $files[] = array_filter([
                'token' => $row['token'] ?? '',
                'path' => $row['path'] ?? '',
                'url' => $row['url'] ?? '',
                'mime' => $row['mime'] ?? '',
                'size_bytes' => $row['size_bytes'] ?? '',
                'status' => $row['status'] ?? '',
            ], function ($value) {
                return $value !== '' && $value !== null;
            });
        }
        return $files;
    }

    private function deleteFilesForSubmission(int $submission_id): bool
    {
        global $wpdb;
        $table = $this->config->getTable('files');
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT id, path FROM %i WHERE submission_id = %d",
                $table,
                $submission_id
            ),
            ARRAY_A
        );

        if (!is_array($rows) || empty($rows)) {
            return true;
        }

        $uploads = wp_get_upload_dir();
        $uploads_base = isset($uploads['basedir']) ? (string) $uploads['basedir'] : '';
        $uploads_base_norm = $uploads_base !== '' ? wp_normalize_path(trailingslashit($uploads_base)) : '';

        $ok = true;
        foreach ($rows as $row) {
            $id = (int) ($row['id'] ?? 0);
            $path = (string) ($row['path'] ?? '');
            if ($id <= 0) {
                continue;
            }

            if ($path !== '' && $uploads_base_norm !== '') {
                $file_path_norm = wp_normalize_path($path);
                if (strpos($file_path_norm, $uploads_base_norm) === 0) {
                    wp_delete_file($path);
                }
            }

            $deleted = $wpdb->delete($table, ['id' => $id], ['%d']);
            if (!$deleted) {
                $ok = false;
            }
        }

        return $ok;
    }

    private function redactPaymentMeta(array $payment_meta): array
    {
        $redacted = $payment_meta;
        if (isset($redacted['stripe']['client_secret'])) {
            $redacted['stripe']['client_secret'] = '[redacted]';
        }
        if (isset($redacted['stripe']['payment_method'])) {
            $redacted['stripe']['payment_method'] = '[redacted]';
        }
        return $redacted;
    }

    private function anonymizeSubmissionPayload($payload): array
    {
        if (!is_array($payload)) {
            return [];
        }

        $anonymized = [];
        foreach ($payload as $key => $value) {
            if (is_array($value)) {
                $anonymized[$key] = $this->anonymizeSubmissionPayload($value);
                continue;
            }
            if (is_bool($value)) {
                $anonymized[$key] = false;
                continue;
            }
            if (is_numeric($value)) {
                $anonymized[$key] = 0;
                continue;
            }
            if ($value === null) {
                $anonymized[$key] = null;
                continue;
            }
            $anonymized[$key] = '[redacted]';
        }

        return $anonymized;
    }

    private function maskIp(string $ip): string
    {
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $parts = explode('.', $ip);
            if (count($parts) === 4) {
                $parts[3] = '***';
                return implode('.', $parts);
            }
            return $ip;
        }

        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            $packed = @inet_pton($ip);
            if ($packed === false) {
                return $ip;
            }
            $hex = bin2hex($packed);
            $masked = substr($hex, 0, 16) . str_repeat('0', 16);
            $masked_ip = @inet_ntop(hex2bin($masked));
            return $masked_ip ? $masked_ip . '::****' : $ip;
        }

        return $ip;
    }

    private function summarizeUserAgent(string $ua): string
    {
        $ua = trim($ua);
        if ($ua === '') {
            return '';
        }

        $browser = '';
        $version = '';

        if (preg_match('/Edg\\/([\\d\\.]+)/i', $ua, $match)) {
            $browser = 'Edge';
            $version = $match[1];
        } elseif (preg_match('/Chrome\\/([\\d\\.]+)/i', $ua, $match) && stripos($ua, 'Edg/') === false) {
            $browser = 'Chrome';
            $version = $match[1];
        } elseif (preg_match('/Firefox\\/([\\d\\.]+)/i', $ua, $match)) {
            $browser = 'Firefox';
            $version = $match[1];
        } elseif (preg_match('/Version\\/([\\d\\.]+).*Safari/i', $ua, $match)) {
            $browser = 'Safari';
            $version = $match[1];
        }

        $os = '';
        if (preg_match('/Mac OS X ([\\d_\\.]+)/i', $ua, $match)) {
            $os = 'macOS ' . str_replace('_', '.', $match[1]);
        } elseif (preg_match('/Windows NT ([\\d\\.]+)/i', $ua, $match)) {
            $os = 'Windows ' . $match[1];
        } elseif (preg_match('/Android ([\\d\\.]+)/i', $ua, $match)) {
            $os = 'Android ' . $match[1];
        } elseif (preg_match('/iPhone OS ([\\d_]+)/i', $ua, $match)) {
            $os = 'iOS ' . str_replace('_', '.', $match[1]);
        } elseif (preg_match('/iPad; CPU OS ([\\d_]+)/i', $ua, $match)) {
            $os = 'iPadOS ' . str_replace('_', '.', $match[1]);
        }

        $parts = [];
        if ($browser !== '') {
            $parts[] = trim($browser . ' ' . $version);
        }
        if ($os !== '') {
            $parts[] = $os;
        }

        return $parts ? implode(' · ', $parts) : '';
    }
}
