<?php
/**
 * Dashboard Controller V1
 *
 * @package ZenoChatbot\Http\Controller\Api\V1
 */

namespace ZenoChatbot\Http\Controller\Api\V1;

use ZenoChatbot\Framework\Support\Collection;
use ZenoChatbot\Model\Conversation;
use ZenoChatbot\Model\Message;
use ZenoChatbot\Services\Dashboard\Widget;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Dashboard API controller for statistics and overview data
 */
class DashboardController
{
    /**
     * Default chart window when no explicit range is requested.
     */
    private const DEFAULT_RANGE_DAYS = 30;

    /**
     * @param mixed $date
     * @return string
     */
    private static function sanitizeDateInput($date)
    {
        return sanitize_text_field((string) $date);
    }

    /**
     * Resolve and validate the requested date range.
     *
     * @param \WP_REST_Request $request
     * @return array{start?: int, end?: int, error?: array} Timestamps on success, 'error' response array on failure.
     */
    private static function resolveDateRange(\WP_REST_Request $request)
    {
        $start_date = static::sanitizeDateInput($request->get_param('start_date'));
        $end_date = static::sanitizeDateInput($request->get_param('end_date'));

        // Default window if no dates provided
        if (empty($start_date) || empty($end_date)) {
            $end_date = gmdate('Y-m-d');
            $start_date = gmdate('Y-m-d', strtotime('-' . (static::DEFAULT_RANGE_DAYS - 1) . ' days'));
        }

        $start_timestamp = strtotime($start_date);
        $end_timestamp = strtotime($end_date);

        if (!$start_timestamp || !$end_timestamp) {
            return ['error' => [
                'success' => false,
                'message' => 'Invalid date format. Use Y-m-d format.',
                'status'  => 400
            ]];
        }

        if ($start_timestamp > $end_timestamp) {
            return ['error' => [
                'success' => false,
                'message' => 'start_date must be on or before end_date.',
                'status'  => 400
            ]];
        }

        return ['start' => $start_timestamp, 'end' => $end_timestamp];
    }

    /**
     * Build per-day labels and values for a range, filling days with no data.
     *
     * @param int $start_timestamp
     * @param int $end_timestamp
     * @param array<string, int> $totalsByDay Map of 'Y-m-d' => value
     * @return array{labels: array, data: array}
     */
    private static function buildDailySeries($start_timestamp, $end_timestamp, array $totalsByDay)
    {
        $labels = [];
        $data = [];

        for ($ts = $start_timestamp; $ts <= $end_timestamp; $ts = strtotime('+1 day', $ts)) {
            $labels[] = gmdate('M j', $ts);
            $data[] = $totalsByDay[gmdate('Y-m-d', $ts)] ?? 0;
        }

        return ['labels' => $labels, 'data' => $data];
    }

    /**
     * Get dashboard statistics
     *
     * @param \WP_REST_Request $request
     * @return array
     */
    public function stats(\WP_REST_Request $request)
    {
        try {
            return [
                'success' => true,
                'data'    => Widget::getWidgets()
            ];

        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => 'Failed to retrieve dashboard statistics',
                'error'   => WP_DEBUG ? $e->getMessage() : 'Internal server error',
                'status'  => 500
            ];
        }
    }

    /**
     * Get chart data for conversations within a date range
     *
     * @param \WP_REST_Request $request
     * @return array
     */
    public function chartData(\WP_REST_Request $request)
    {
        try {
            $range = static::resolveDateRange($request);

            if (isset($range['error'])) {
                return $range['error'];
            }

            global $wpdb;

            $table = esc_sql($wpdb->prefix . 'zenochatbot_conversations');

            // One grouped query for the whole range (previously one COUNT per day)
            // Safe: %s placeholders via $wpdb->prepare(); table name from $wpdb->prefix, esc_sql()'d.
            // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $rows = $wpdb->get_results($wpdb->prepare(
                "SELECT DATE(started_at) AS day, COUNT(*) AS total
                 FROM {$table}
                 WHERE started_at >= %s AND started_at <= %s
                 GROUP BY DATE(started_at)",
                gmdate('Y-m-d 00:00:00', $range['start']),
                gmdate('Y-m-d 23:59:59', $range['end'])
            ), ARRAY_A);
            // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

            $totalsByDay = [];
            foreach ($rows ?: [] as $row) {
                $totalsByDay[$row['day']] = (int) $row['total'];
            }

            $series = static::buildDailySeries($range['start'], $range['end'], $totalsByDay);

            return [
                'success' => true,
                'data'    => [
                    'labels'     => $series['labels'],
                    'data'       => $series['data'],
                    'start_date' => gmdate('Y-m-d', $range['start']),
                    'end_date'   => gmdate('Y-m-d', $range['end'])
                ]
            ];

        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => 'Failed to retrieve chart data',
                'error'   => WP_DEBUG ? $e->getMessage() : 'Internal server error',
                'status'  => 500
            ];
        }
    }

    /**
     * Get token usage chart data within a date range
     *
     * @param \WP_REST_Request $request
     * @return array
     */
    public function tokenUsageChartData(\WP_REST_Request $request)
    {
        try {
            $range = static::resolveDateRange($request);

            if (isset($range['error'])) {
                return $range['error'];
            }

            global $wpdb;

            $table = esc_sql($wpdb->prefix . 'zenochatbot_messages');

            // One grouped query for the whole range (previously one SUM per day)
            // Safe: %s placeholders via $wpdb->prepare(); table name from $wpdb->prefix, esc_sql()'d.
            // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $rows = $wpdb->get_results($wpdb->prepare(
                "SELECT DATE(created_at) AS day, SUM(token_usage) AS total
                 FROM {$table}
                 WHERE created_at >= %s AND created_at <= %s
                   AND sender_type = 'ai'
                 GROUP BY DATE(created_at)",
                gmdate('Y-m-d 00:00:00', $range['start']),
                gmdate('Y-m-d 23:59:59', $range['end'])
            ), ARRAY_A);
            // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

            $totalsByDay = [];
            foreach ($rows ?: [] as $row) {
                $totalsByDay[$row['day']] = (int) $row['total'];
            }

            $series = static::buildDailySeries($range['start'], $range['end'], $totalsByDay);

            return [
                'success' => true,
                'data'    => [
                    'labels'     => $series['labels'],
                    'data'       => $series['data'],
                    'start_date' => gmdate('Y-m-d', $range['start']),
                    'end_date'   => gmdate('Y-m-d', $range['end'])
                ]
            ];

        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => 'Failed to retrieve token usage data',
                'error'   => WP_DEBUG ? $e->getMessage() : 'Internal server error',
                'status'  => 500
            ];
        }
    }

    /**
     * Get recent conversations for dashboard sidebar
     *
     * @param \WP_REST_Request $request
     * @return Collection|array
     */
    public function recentConversations(\WP_REST_Request $request)
    {
        $limit = max(1, min(50, (int) $request->get_param('limit') ?: 5));

        $latestConversations = Conversation::query()
            ->with(['customer'])
            ->latest()
            ->limit($limit)
            ->get();

        static::attachFirstMessage($latestConversations);

        return [
            'success'       => true,
            'conversations' => $latestConversations,
        ];
    }

    /**
     * Attach the opening message of each conversation as a one-item
     * 'messages' collection (the shape the dashboard UI expects).
     *
     * Two queries total, regardless of conversation count — the previous
     * eager load with limit(1) ran one messages query per conversation.
     *
     * @param Collection $conversations
     * @return void
     */
    private static function attachFirstMessage(Collection $conversations)
    {
        $ids = [];
        foreach ($conversations as $conversation) {
            $ids[] = (int) $conversation->id;
        }

        if (empty($ids)) {
            return;
        }

        global $wpdb;

        $table = esc_sql($wpdb->prefix . 'zenochatbot_messages');
        $placeholders = implode(',', array_fill(0, count($ids), '%d'));

        // Lowest id = first inserted message of the conversation
        // Safe: $placeholders is a generated `%d,%d,...` list bound via $wpdb->prepare();
        // table name from $wpdb->prefix, esc_sql()'d. Sniff can't see dynamic placeholders.
        // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $firstIds = $wpdb->get_col($wpdb->prepare(
            "SELECT MIN(id) FROM {$table} WHERE conversation_id IN ({$placeholders}) GROUP BY conversation_id",
            $ids
        ));
        // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

        $messagesByConversation = [];
        if (!empty($firstIds)) {
            $messages = Message::whereIn('id', array_map('intval', $firstIds))->get();
            foreach ($messages as $message) {
                $messagesByConversation[(int) $message->conversation_id] = $message;
            }
        }

        foreach ($conversations as $conversation) {
            $message = $messagesByConversation[(int) $conversation->id] ?? null;
            $conversation->messages = new Collection($message ? [$message] : []);
        }
    }
}
