<?php
namespace BLR\Backline\Core;

use BLR\Backline\Modules\Contacts\NewsletterDoubleOptIn;

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

final class Privacy {
    private const CONTACTS_EXPORTER_KEY = 'blr-backline-contacts';
    private const OUTREACH_EXPORTER_KEY = 'blr-backline-outreach';
    private const EXPORT_BATCH_SIZE = 25;
    private const ERASER_BATCH_SIZE = 25;

    public static function boot(): void {
        add_filter('wp_privacy_personal_data_exporters', [self::class, 'register_exporter']);
        add_filter('wp_privacy_personal_data_erasers', [self::class, 'register_eraser']);
    }

    public static function register_exporter(array $exporters): array {
        $exporters[self::CONTACTS_EXPORTER_KEY] = [
            'exporter_friendly_name' => __('BLR Backline Contacts', 'blr-backline'),
            'callback' => [self::class, 'export_contact_personal_data'],
        ];

        $exporters[self::OUTREACH_EXPORTER_KEY] = [
            'exporter_friendly_name' => __('BLR Backline Outreach Logs', 'blr-backline'),
            'callback' => [self::class, 'export_outreach_personal_data'],
        ];

        return $exporters;
    }

    public static function register_eraser(array $erasers): array {
        $erasers[self::CONTACTS_EXPORTER_KEY] = [
            'eraser_friendly_name' => __('BLR Backline Contacts', 'blr-backline'),
            'callback' => [self::class, 'erase_contact_personal_data'],
        ];

        $erasers[self::OUTREACH_EXPORTER_KEY] = [
            'eraser_friendly_name' => __('BLR Backline Outreach Logs', 'blr-backline'),
            'callback' => [self::class, 'erase_outreach_personal_data'],
        ];

        return $erasers;
    }

    public static function export_personal_data(string $email_address, int $page = 1): array {
        $contacts = self::export_contact_personal_data($email_address, $page);
        $outreach = self::export_outreach_personal_data($email_address, $page);

        return [
            'data' => array_merge((array) ($contacts['data'] ?? []), (array) ($outreach['data'] ?? [])),
            'done' => !empty($contacts['done']) && !empty($outreach['done']),
        ];
    }

    public static function erase_personal_data(string $email_address, int $page = 1): array {
        $contacts = self::erase_contact_personal_data($email_address, $page);
        $outreach = self::erase_outreach_personal_data($email_address, $page);

        return [
            'items_removed' => !empty($contacts['items_removed']) || !empty($outreach['items_removed']),
            'items_retained' => !empty($contacts['items_retained']) || !empty($outreach['items_retained']),
            'messages' => array_values(array_filter(array_merge((array) ($contacts['messages'] ?? []), (array) ($outreach['messages'] ?? [])), 'strlen')),
            'done' => !empty($contacts['done']) && !empty($outreach['done']),
        ];
    }

    public static function export_contact_personal_data(string $email_address, int $page = 1): array {
        $email = sanitize_email($email_address);
        if ($email === '') {
            return ['data' => [], 'done' => true];
        }

        $page = max(1, $page);
        $offset = ($page - 1) * self::EXPORT_BATCH_SIZE;
        $contacts = self::find_contacts_by_email($email, self::EXPORT_BATCH_SIZE + 1, $offset);
        $done = count($contacts) <= self::EXPORT_BATCH_SIZE;

        if (!$done) {
            array_pop($contacts);
        }

        $items = [];
        foreach ($contacts as $contact) {
            $items[] = [
                'group_id' => self::CONTACTS_EXPORTER_KEY,
                'group_label' => __('BLR Backline Contacts', 'blr-backline'),
                'item_id' => 'blr-contact-' . (int) $contact->ID,
                'data' => self::export_contact_data((int) $contact->ID),
            ];
        }

        return [
            'data' => $items,
            'done' => $done,
        ];
    }

    public static function export_outreach_personal_data(string $email_address, int $page = 1): array {
        $email = sanitize_email($email_address);
        if ($email === '') {
            return ['data' => [], 'done' => true];
        }

        $page = max(1, $page);
        $offset = ($page - 1) * self::EXPORT_BATCH_SIZE;
        $logs = self::find_logs_by_email($email, self::EXPORT_BATCH_SIZE + 1, $offset);
        $done = count($logs) <= self::EXPORT_BATCH_SIZE;

        if (!$done) {
            array_pop($logs);
        }

        $items = [];
        foreach ($logs as $log) {
            $items[] = [
                'group_id' => self::OUTREACH_EXPORTER_KEY,
                'group_label' => __('BLR Backline Outreach', 'blr-backline'),
                'item_id' => 'blr-outreach-log-' . (int) $log->ID,
                'data' => self::export_log_data((int) $log->ID),
            ];
        }

        return [
            'data' => $items,
            'done' => $done,
        ];
    }

    public static function erase_contact_personal_data(string $email_address, int $page = 1): array {
        $email = sanitize_email($email_address);
        if ($email === '') {
            return self::empty_eraser_response(true);
        }

        // Do not use an offset while deleting. The matching result set shrinks
        // after every batch, so offset-based pagination can skip records.
        $contacts = self::find_contacts_by_email($email, self::ERASER_BATCH_SIZE + 1, 0);
        $done = count($contacts) <= self::ERASER_BATCH_SIZE;
        if (!$done) {
            array_pop($contacts);
        }

        $items_removed = false;
        $items_retained = false;
        $messages = [];

        foreach ($contacts as $contact) {
            $deleted = wp_delete_post((int) $contact->ID, true);
            if ($deleted) {
                $items_removed = true;
            } else {
                $items_retained = true;
                $messages[] = sprintf(
                    /* translators: %d: contact post ID. */
                    __('Could not delete Backline contact ID %d.', 'blr-backline'),
                    (int) $contact->ID
                );
            }
        }

        if ($items_removed) {
            $messages[] = __('BLR Backline contact records were removed.', 'blr-backline');
        }

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

    public static function erase_outreach_personal_data(string $email_address, int $page = 1): array {
        $email = sanitize_email($email_address);
        if ($email === '') {
            return self::empty_eraser_response(true);
        }

        // Do not use an offset while anonymizing. The matching result set shrinks
        // after each batch because anonymized logs no longer match the email.
        $logs = self::find_logs_by_email($email, self::ERASER_BATCH_SIZE + 1, 0);
        $done = count($logs) <= self::ERASER_BATCH_SIZE;
        if (!$done) {
            array_pop($logs);
        }

        $items_removed = false;
        foreach ($logs as $log) {
            self::anonymize_log((int) $log->ID, $email);
            $items_removed = true;
        }

        return [
            'items_removed' => $items_removed,
            'items_retained' => false,
            'messages' => $items_removed ? [__('BLR Backline outreach logs were anonymized.', 'blr-backline')] : [],
            'done' => $done,
        ];
    }

    private static function empty_eraser_response(bool $done): array {
        return [
            'items_removed' => false,
            'items_retained' => false,
            'messages' => [],
            'done' => $done,
        ];
    }

    private static function find_contacts_by_email(string $email, int $limit, int $offset): array {
        return get_posts([
            'post_type' => 'blr_contact',
            'post_status' => ['publish', 'draft', 'pending', 'private', 'future', 'trash'],
            'posts_per_page' => $limit,
            'offset' => $offset,
            'orderby' => 'ID',
            'order' => 'ASC',
            'meta_key' => 'blr_contact_email',
            'meta_value' => $email,
        ]);
    }

    private static function find_logs_by_email(string $email, int $limit, int $offset): array {
        return get_posts([
            'post_type' => 'blr_outreach_log',
            'post_status' => ['publish', 'draft', 'pending', 'private', 'future', 'trash'],
            'posts_per_page' => $limit,
            'offset' => $offset,
            'orderby' => 'ID',
            'order' => 'ASC',
            'meta_key' => 'blr_outreach_log_email',
            'meta_value' => $email,
        ]);
    }

    private static function export_contact_data(int $contact_id): array {
        $data = [
            ['name' => __('Contact Title', 'blr-backline'), 'value' => (string) get_the_title($contact_id)],
            ['name' => __('Email', 'blr-backline'), 'value' => (string) get_post_meta($contact_id, 'blr_contact_email', true)],
            ['name' => __('Phone', 'blr-backline'), 'value' => (string) get_post_meta($contact_id, 'blr_contact_phone', true)],
            ['name' => __('Company', 'blr-backline'), 'value' => (string) get_post_meta($contact_id, 'blr_contact_company', true)],
            ['name' => __('Role', 'blr-backline'), 'value' => (string) get_post_meta($contact_id, 'blr_contact_role', true)],
            ['name' => __('City', 'blr-backline'), 'value' => (string) get_post_meta($contact_id, 'blr_contact_city', true)],
            ['name' => __('State', 'blr-backline'), 'value' => (string) get_post_meta($contact_id, 'blr_contact_state', true)],
            ['name' => __('Website', 'blr-backline'), 'value' => (string) get_post_meta($contact_id, 'blr_contact_website', true)],
            ['name' => __('Newsletter Status', 'blr-backline'), 'value' => NewsletterDoubleOptIn::status_label(NewsletterDoubleOptIn::get_status($contact_id))],
            ['name' => __('Newsletter Signup Requested (GMT)', 'blr-backline'), 'value' => (string) get_post_meta($contact_id, NewsletterDoubleOptIn::META_REQUESTED_GMT, true)],
            ['name' => __('Newsletter Signup Confirmed (GMT)', 'blr-backline'), 'value' => (string) get_post_meta($contact_id, NewsletterDoubleOptIn::META_CONFIRMED_GMT, true)],
            ['name' => __('Notes', 'blr-backline'), 'value' => wp_strip_all_tags((string) get_post_field('post_content', $contact_id))],
            ['name' => __('Internal Notes', 'blr-backline'), 'value' => wp_strip_all_tags((string) get_post_meta($contact_id, 'blr_contact_notes', true))],
        ];

        return array_values(array_filter($data, static function (array $row): bool {
            return trim((string) ($row['value'] ?? '')) !== '';
        }));
    }

    private static function export_log_data(int $log_id): array {
        $campaign_id = (int) wp_get_post_parent_id($log_id);
        $data = [
            ['name' => __('Campaign', 'blr-backline'), 'value' => $campaign_id > 0 ? (string) get_the_title($campaign_id) : ''],
            ['name' => __('Email', 'blr-backline'), 'value' => (string) get_post_meta($log_id, 'blr_outreach_log_email', true)],
            ['name' => __('Status', 'blr-backline'), 'value' => (string) get_post_meta($log_id, 'blr_outreach_log_status', true)],
            ['name' => __('Sent At', 'blr-backline'), 'value' => (string) get_post_meta($log_id, 'blr_outreach_log_sent_at', true)],
            ['name' => __('Note', 'blr-backline'), 'value' => wp_strip_all_tags((string) get_post_meta($log_id, 'blr_outreach_log_note', true))],
            ['name' => __('Error', 'blr-backline'), 'value' => (string) get_post_meta($log_id, 'blr_outreach_log_error', true)],
        ];

        return array_values(array_filter($data, static function (array $row): bool {
            return trim((string) ($row['value'] ?? '')) !== '';
        }));
    }

    private static function anonymize_log(int $log_id, string $email): void {
        $replacement = 'erased+' . md5(strtolower($email) . ':' . $log_id) . '@example.invalid';
        wp_update_post(wp_slash([
            'ID' => $log_id,
            /* translators: %s: Short anonymized outreach log identifier. */
            'post_title' => sprintf(__('Erased Contact — %s', 'blr-backline'), substr(md5((string) $log_id), 0, 8)),
        ]));
        update_post_meta($log_id, 'blr_outreach_log_email', $replacement);
        update_post_meta($log_id, 'blr_outreach_log_contact_id', 0);
        update_post_meta($log_id, 'blr_outreach_log_note', '');
        update_post_meta($log_id, 'blr_outreach_log_error', '');
        update_post_meta($log_id, 'blr_outreach_log_last_updated', current_time('mysql'));
    }
}
