<?php

/**
 * Retention Logs REST Controller
 *
 * Provides paginated activity log data for the retention analytics page.
 *
 * @package ArraySubs\Features\RetentionAnalytics\REST
 * @since 1.9.0
 */

namespace ArraySubs\Features\RetentionAnalytics\REST;

defined('ABSPATH') || exit;

use ArraySubs\Features\RetentionAnalytics\Migrations\CreateRetentionLogsTable;
use ArraySubs\Supports\BaseRestController;
use WP_REST_Request;
use WP_REST_Response;

/**
 * Logs controller — paginated activity log
 */
class LogsController extends BaseRestController
{
    /**
     * Enable auto-loading
     *
     * @var bool
     */
    public static $loadable = true;

    /**
     * Allowed event types for filtering
     *
     * @var array
     */
    const ALLOWED_EVENT_TYPES = [
        'cancelled',
        'scheduled_cancel',
        'cancel_undone',
        'offer_shown',
        'offer_accepted',
        'offer_declined',
    ];

    /**
     * Constructor
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Register REST routes
     *
     * @return void
     */
    public function registerRoutes(): void
    {
        \register_rest_route(
            $this->namespace,
            '/retention-analytics/logs',
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'getLogs'],
                'permission_callback' => [$this, 'checkAdminPermission'],
                'args'                => [
                    'start_date'  => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_text_field',
                        'default'           => \gmdate('Y-m-d', \time() - (30 * DAY_IN_SECONDS)),
                    ],
                    'end_date'    => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_text_field',
                        'default'           => \gmdate('Y-m-d'),
                    ],
                    'product_ids' => [
                        'type'    => 'string',
                        'default' => '',
                    ],
                    'event_type'  => [
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_key',
                        'default'           => '',
                    ],
                    'page'        => [
                        'type'              => 'integer',
                        'sanitize_callback' => 'absint',
                        'default'           => 1,
                    ],
                    'per_page'    => [
                        'type'              => 'integer',
                        'sanitize_callback' => 'absint',
                        'default'           => 20,
                    ],
                ],
            ]
        );
    }

    /**
     * Check admin permission
     *
     * @return bool
     */
    public function checkAdminPermission(): bool
    {
        if (!\is_user_logged_in()) {
            return false;
        }

        return \current_user_can('manage_woocommerce') || \current_user_can('manage_options');
    }

    /**
     * Get paginated log entries
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function getLogs(WP_REST_Request $request): WP_REST_Response
    {
        global $wpdb;

        $start_date  = $this->sanitizeDate($request->get_param('start_date'));
        $end_date    = $this->sanitizeDate($request->get_param('end_date'));
        $product_ids = $this->sanitizeProductIds($request->get_param('product_ids'));
        $event_type  = \sanitize_key($request->get_param('event_type'));
        $page        = max(1, \absint($request->get_param('page')));
        $per_page    = min(50, max(1, \absint($request->get_param('per_page'))));
        $offset      = ($page - 1) * $per_page;

        $table      = CreateRetentionLogsTable::getTableName();
        $safe_table = \esc_sql($table);
        $where      = array('rl.created_at BETWEEN %s AND %s');
        $args       = array($start_date . ' 00:00:00', $end_date . ' 23:59:59');

        // Event type filter.
        if (!empty($event_type) && in_array($event_type, self::ALLOWED_EVENT_TYPES, true)) {
            $where[] = 'rl.event_type = %s';
            $args[]  = $event_type;
        }

        // Product filter.
        if (!empty($product_ids)) {
            $placeholders = implode(',', array_fill(0, count($product_ids), '%d'));
            $where[]      = 'rl.effective_product_id IN (' . $placeholders . ')';
            $args         = array_merge($args, $product_ids);
        }

        $where_sql = implode(' AND ', $where);

        // Total count.
        // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
        $total = (int) $wpdb->get_var(
            $wpdb->prepare(
                'SELECT COUNT(*) FROM ' . $safe_table . ' rl WHERE ' . $where_sql,
                ...$args
            )
        );
        // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
        // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

        // Paginated rows.
        $limit_args = array_merge($args, array($per_page, $offset));

        // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                'SELECT rl.* FROM ' . $safe_table . ' rl
                 WHERE ' . $where_sql . '
                 ORDER BY rl.created_at DESC
                 LIMIT %d OFFSET %d',
                ...$limit_args
            )
        );
        // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
        // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

        $items = [];
        foreach ($rows as $row) {
            $items[] = [
                'id'               => (int) $row->id,
                'date'             => $row->created_at,
                'created_at'       => $row->created_at,
                'subscription_id'  => (int) $row->subscription_id,
                'customer_id'      => (int) $row->customer_id,
                'customer_name'    => $row->customer_name_snapshot,
                'customer_name_snapshot' => $row->customer_name_snapshot,
                'product_name'     => $row->product_name_snapshot,
                'product_name_snapshot' => $row->product_name_snapshot,
                'product_id'       => (int) $row->effective_product_id,
                'effective_product_id' => (int) $row->effective_product_id,
                'event_type'       => $row->event_type,
                'reason'           => $row->reason,
                'offer_type'       => $row->offer_type,
                'initiated_by'     => $row->initiated_by,
                'subscription_age' => (int) $row->subscription_age,
                'payments_count'   => (int) $row->payments_count,
                'recurring_amount' => (float) $row->recurring_amount,
                'currency'         => $row->currency,
            ];
        }

        return arraysubs_rest_response(
            [
                'items'    => $items,
                'total'    => $total,
                'page'     => $page,
                'per_page' => $per_page,
            ],
            200
        );
    }

    /**
     * Sanitize a Y-m-d date string
     *
     * @param mixed $date Date input.
     * @return string
     */
    private function sanitizeDate($date): string
    {
        $date = \sanitize_text_field((string) $date);

        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
            return $date;
        }

        return '';
    }

    /**
     * Sanitize product_ids parameter into an array of integers
     *
     * @param mixed $ids Input product IDs.
     * @return array
     */
    private function sanitizeProductIds($ids): array
    {
        if (empty($ids)) {
            return [];
        }

        if (is_string($ids)) {
            $ids = explode(',', $ids);
        }

        if (!is_array($ids)) {
            return [];
        }

        return array_filter(array_map('absint', $ids));
    }
}
