<?php

declare(strict_types=1);
defined('ABSPATH') || exit;

class DSProofwp_Widgets_Controller
{
    private const ROUTE_NAMESPACE = 'dsproofwp/v1';

    private DSProofwp_Widget_Repository $widget_repository;
    private DSProofwp_Widget_Review_Repository $widget_review_repository;
    private DSProofwp_Review_Repository $review_repository;
    private DSProofwp_Review_Media_Repository $media_repository;

    public function __construct(
        DSProofwp_Widget_Repository $widget_repository,
        DSProofwp_Widget_Review_Repository $widget_review_repository,
        DSProofwp_Review_Repository $review_repository,
        DSProofwp_Review_Media_Repository $media_repository
    ) {
        $this->widget_repository        = $widget_repository;
        $this->widget_review_repository = $widget_review_repository;
        $this->review_repository        = $review_repository;
        $this->media_repository         = $media_repository;
    }

    public function register_routes(): void
    {
        register_rest_route(
            self::ROUTE_NAMESPACE,
            '/widgets',
            [
                [
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => [$this, 'get_widgets'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'create_widget'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
            ]
        );

        register_rest_route(
            self::ROUTE_NAMESPACE,
            '/widgets/(?P<id>\d+)',
            [
                [
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => [$this, 'get_widget'],
                    'permission_callback' => '__return_true',
                ],
                [
                    'methods'             => WP_REST_Server::EDITABLE,
                    'callback'            => [$this, 'update_widget'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
                [
                    'methods'             => WP_REST_Server::DELETABLE,
                    'callback'            => [$this, 'delete_widget'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
            ]
        );

        register_rest_route(
            self::ROUTE_NAMESPACE,
            '/widgets/bulk-delete',
            [
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'bulk_delete_widgets'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
            ]
        );

        register_rest_route(
            self::ROUTE_NAMESPACE,
            '/widgets/(?P<id>\d+)/duplicate',
            [
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'duplicate_widget'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
            ]
        );
    }

    public function get_widgets(WP_REST_Request $request): WP_REST_Response
    {
        $result = $this->widget_repository->list([
            'status'      => $request->get_param('status'),
            'search'      => $request->get_param('search'),
            'page'        => $request->get_param('page'),
            'per_page'    => $request->get_param('per_page'),
        ]);

        $widgets = $this->attach_review_assignments($result['items']);

        return rest_ensure_response([
            'items'    => $widgets,
            'total'    => $result['total'],
            'page'     => $result['page'],
            'per_page' => $result['per_page'],
        ]);
    }

    public function get_widget(WP_REST_Request $request)
    {
        $widget = $this->widget_repository->find((int) $request['id']);

        if (! $widget) {
            return new WP_Error('dsproofwp_widget_not_found', 'Widget not found.', ['status' => 404]);
        }

        $items = $this->attach_review_assignments([$widget]);
        $widget = $items[0];

        return rest_ensure_response($this->attach_reviews_to_widget($widget));
    }

    public function create_widget(WP_REST_Request $request)
    {
        $payload = $this->sanitize_widget_payload($request);

        if (! isset($payload['title']) || trim((string) $payload['title']) === '') {
            $payload['title'] = $this->widget_repository->get_next_untitled_title();
        }

        $widget  = $this->widget_repository->create($payload);

        if (! $widget) {
            return new WP_Error('dsproofwp_widget_create_failed', 'Failed to create widget.', ['status' => 500]);
        }

        return rest_ensure_response($this->persist_review_assignments($widget, $payload));
    }

    public function update_widget(WP_REST_Request $request)
    {
        $widget_id = (int) $request['id'];

        if (! $this->widget_repository->find($widget_id)) {
            return new WP_Error('dsproofwp_widget_not_found', 'Widget not found.', ['status' => 404]);
        }

        $payload = $this->sanitize_widget_payload($request, true);
        $widget  = $this->widget_repository->update($widget_id, $payload);

        if (! $widget) {
            return new WP_Error('dsproofwp_widget_update_failed', 'Failed to update widget.', ['status' => 500]);
        }

        return rest_ensure_response($this->persist_review_assignments($widget, $payload));
    }

    public function delete_widget(WP_REST_Request $request): WP_REST_Response
    {
        $widget_id = (int) $request['id'];

        $this->widget_review_repository->delete_by_widget_id($widget_id);

        return rest_ensure_response([
            'deleted' => $this->widget_repository->delete($widget_id),
        ]);
    }

    public function bulk_delete_widgets(WP_REST_Request $request)
    {
        $ids = $request->get_param('ids');
        if (! is_array($ids)) {
            return new WP_Error('dsproofwp_invalid_ids', 'IDs must be an array.', ['status' => 400]);
        }

        $deleted_count = 0;
        foreach ($ids as $id) {
            $widget_id = (int) $id;

            $this->widget_review_repository->delete_by_widget_id($widget_id);

            if ($this->widget_repository->delete($widget_id)) {
                $deleted_count++;
            }
        }

        return rest_ensure_response([
            'deleted_count' => $deleted_count,
            'success'       => true,
        ]);
    }

    public function duplicate_widget(WP_REST_Request $request)
    {
        $widget_id = (int) $request['id'];
        $widget = $this->widget_repository->find($widget_id);

        if (! $widget) {
            return new WP_Error('dsproofwp_widget_not_found', 'Widget not found.', ['status' => 404]);
        }

        $widgets = $this->attach_review_assignments([$widget]);
        $widget_with_assignments = $widgets[0];
        $review_ids = $widget_with_assignments['review_ids'] ?? [];

        $payload = [
            'title'            => $widget['title'] . ' (Copy)',
            'widget_type'      => $widget['widget_type'],
            'settings'         => $widget['settings'],
            'review_selection' => $widget['review_selection'],
            'status'           => $widget['status'],
            'review_ids'       => $review_ids,
        ];

        $duplicated_widget = $this->widget_repository->create($payload);

        if (! $duplicated_widget) {
            return new WP_Error('dsproofwp_widget_duplicate_failed', 'Failed to duplicate widget.', ['status' => 500]);
        }

        return rest_ensure_response($this->persist_review_assignments($duplicated_widget, $payload));
    }

    public function manage_permission(): bool
    {
        return current_user_can('manage_options');
    }

    private function attach_review_assignments(array $widgets): array
    {
        if (empty($widgets)) {
            return [];
        }

        $widget_ids = array_map(
            static function (array $widget): int {
                return (int) $widget['id'];
            },
            $widgets
        );

        $assignments_map = $this->widget_review_repository->find_by_widget_ids($widget_ids);

        foreach ($widgets as &$widget) {
            $widget['review_assignments'] = $assignments_map[$widget['id']] ?? [];
            $widget['review_ids']         = array_values(array_map(
                static function (array $assignment): int {
                    return (int) $assignment['review_id'];
                },
                $widget['review_assignments']
            ));
        }

        return $widgets;
    }

    private function persist_review_assignments(array $widget, array $payload): array
    {
        if (! array_key_exists('review_ids', $payload)) {
            $items = $this->attach_review_assignments([$widget]);
            return $items[0];
        }

        $widget['review_assignments'] = $this->widget_review_repository->replace_for_widget(
            (int) $widget['id'],
            $payload['review_ids']
        );
        $widget['review_ids'] = array_values(array_map(
            static function (array $assignment): int {
                return (int) $assignment['review_id'];
            },
            $widget['review_assignments']
        ));

        return $widget;
    }

    private function attach_media_to_reviews(array $reviews): array
    {
        if (empty($reviews)) {
            return [];
        }

        $review_ids = array_map(
            static function (array $review): int {
                return (int) $review['id'];
            },
            $reviews
        );

        $media_map = $this->media_repository->find_by_review_ids($review_ids);

        foreach ($reviews as &$review) {
            $review['media'] = $media_map[$review['id']] ?? [];
        }

        return $reviews;
    }

    private function attach_reviews_to_widget(array $widget): array
    {
        $settings  = $widget['settings'] ?? [];
        $sort_by   = $settings['sort_by'] ?? 'newest';

        $is_visual = null;
        // if ($widget['widget_type'] === 'visual') {
        //     $is_visual = true;
        // } elseif ($widget['widget_type'] === 'testimonial') {
        //     $is_visual = false;
        // }

        $review_ids = $widget['review_ids'] ?? [];

        if ($review_ids === []) {
            $widget['reviews'] = [];

            return $widget;
        }

        $result = $this->review_repository->list([
            'include'    => $review_ids,
            'sort'       => $sort_by,
            'is_visual'  => $is_visual,
            'per_page'   => count($review_ids),
            'page'       => 1,
        ]);

        $reviews     = $this->attach_media_to_reviews($result['items']);
        $reviews_map = [];

        foreach ($reviews as $review) {
            $reviews_map[(int) $review['id']] = $review;
        }

        $widget['reviews'] = [];

        foreach ($review_ids as $review_id) {
            if (isset($reviews_map[$review_id])) {
                $widget['reviews'][] = $reviews_map[$review_id];
            }
        }

        return $widget;
    }

    private function sanitize_widget_payload(WP_REST_Request $request, bool $partial = false): array
    {
        $payload = [];

        foreach (['title', 'widget_type', 'settings', 'status'] as $field) {
            $value = $request->get_param($field);

            if ($partial && $value === null) {
                continue;
            }

            $payload[$field] = $value;
        }

        $review_ids = $request->get_param('review_ids');
        if ($review_ids !== null) {
            $payload['review_ids'] = is_array($review_ids) ? $review_ids : [];
        }

        return $payload;
    }
}
