<?php

namespace FluentCrm\App\Http\Controllers;

use FluentCrm\App\Models\Company;
use FluentCrm\App\Models\Funnel;
use FluentCrm\App\Models\Tag;
use FluentCrm\App\Models\Lists;
use FluentCrm\App\Services\Helper;
use FluentCrm\App\Models\Campaign;
use FluentCrm\Framework\Http\Request\Request;
use FluentCrm\App\Services\ContactsQuery;
use FluentCrm\App\Models\SubscriberNote;
use FluentCrm\App\Services\PermissionManager;

/**
 *  OptionsController - REST API Handler Class
 *
 *  REST API Handler
 *
 * @package FluentCrm\App\Http
 *
 * @version 1.0.0
 */
class OptionsController extends Controller
{
    /**
     * Get options based on the requested fields.
     *
     * @return array options.
     * @throws \Exception
     */
    public function index()
    {
        if ($fileds = $this->request->get('fields')) {
            $options = array_unique(explode(',', $fileds));

            $response = [];

            foreach ($options as $method) {
                if (method_exists($this, $method)) {
                    $result = $this->{$method}();
                    $response = array_merge($response, $result);
                }
            }

            return [
                'options' => $response
            ];
        }

        throw new \Exception('Missing requested fields field.', 422);
    }

    /**
     * Include the countries options.
     *
     * @return array
     */
    public function countries()
    {
        /**
         * Determine the list of countries in FluentCRM.
         *
         * This filter allows you to modify the list of countries used in FluentCRM.
         *
         * @param array An array of countries.
         * @since 2.7.0
         *
         */
        $countries = apply_filters('fluent_crm/countries', []);
        $formattedCountries = [];
        foreach ($countries as $country) {
            $country['id'] = $country['code'];
            $country['slug'] = $country['code'];
            $formattedCountries[] = $country;
        }
        return [
            'countries' => $formattedCountries
        ];
    }

    /**
     * Include all the lists.
     *
     * @return array
     */
    public function lists()
    {
        $lists = Lists::select(['id', 'slug', 'title'])->orderBy('title', 'ASC')->get();

        $withCount = (array)$this->request->get('with_count', []);

        if ($withCount && in_array('lists', $withCount, true)) {
            $subscribedCounts = $this->getSubscribedCountByListIds($lists->pluck('id')->all());

            foreach ($lists as $list) {
                $listId = (int)$list->id;
                $list->subscribersCount = isset($subscribedCounts[$listId]) ? $subscribedCounts[$listId] : 0;
            }
        }

        return [
            'lists' => $lists
        ];
    }

    private function getSubscribedCountByListIds($listIds = [])
    {
        $listIds = array_unique(array_filter(array_map('intval', (array)$listIds)));

        if (!$listIds) {
            return [];
        }

        $countRows = fluentCrmDb()->table('fc_subscriber_pivot')
            ->select([
                'fc_subscriber_pivot.object_id',
                fluentCrmDb()->raw('count(*) as total')
            ])
            ->join('fc_subscribers', 'fc_subscribers.id', '=', 'fc_subscriber_pivot.subscriber_id')
            ->where('fc_subscriber_pivot.object_type', Lists::class)
            ->whereIn('fc_subscriber_pivot.object_id', $listIds)
            ->where('fc_subscribers.status', 'subscribed')
            ->groupBy('fc_subscriber_pivot.object_id')
            ->get();

        $counts = [];
        foreach ($countRows as $countRow) {
            $counts[(int)$countRow->object_id] = (int)$countRow->total;
        }

        return $counts;
    }

    /**
     * Include all the tags.
     *
     * @return array
     */
    public function tags()
    {
        $tags = Tag::select(['id', 'slug', 'title'])->orderBy('title', 'ASC')->get();
        foreach ($tags as $tag) {
            $tag->value = strval($tag->id);
            $tag->label = $tag->title;
        }
        return [
            'tags' => $tags
        ];
    }

    /**
     * Include all the Campaigns.
     *
     * @return array
     */
    public function campaigns()
    {
        return [
            'campaigns' => Campaign::select('id', 'title')->orderBy('id', 'DESC')->get()
        ];
    }

    /**
     * Include all the EmailSequences.
     *
     * @return array
     */
    public function email_sequences()
    {
        $sequences = [];

        if (defined('FLUENTCAMPAIGN')) {
            $sequences = \FluentCampaign\App\Models\Sequence::select('id', 'title')->orderBy('id', 'DESC')->get();
        }

        return [
            'email_sequences' => $sequences
        ];
    }

    /**
     * Include all the Automation Funnels.
     *
     * @return array
     */
    public function automation_funnels()
    {
        $funnels = Funnel::select('id', 'title', 'status')
            ->orderBy('id', 'DESC')->get();

        foreach ($funnels as $funnel) {
            $funnel->title .= ' (' . $funnel->status . ')';
        }

        return [
            'automation_funnels' => $funnels
        ];
    }

    /**
     * Include all the Companies.
     *
     * @return array
     */
    public function companies()
    {
        return [
            'companies' => Company::select('id', 'name as title')->orderBy('id', 'DESC')->get()
        ];
    }

    /**
     * Include subscriber statuses.
     *
     * @return array
     */
    public function statuses()
    {
        return [
            'statuses' => fluentcrm_subscriber_statuses(true)
        ];
    }

    /**
     * Include subscribers' sms statuses.
     *
     * @return array
     */
    public function sms_statuses()
    {
        /**
         * sms statuses are static data and no db call is happening here
         * also available in fcAdmin data in frontend
         *
         */
        return [
            'sms_statuses' => fluentcrm_subscriber_sms_statuses(true)
        ];
    }

    /**
     * Include subscriber editable statuses.
     *
     * @return array
     */
    public function editable_statuses()
    {
        return [
            'editable_statuses' => fluentcrm_subscriber_editable_statuses(true)
        ];
    }

    /**
     * Include subscriber Contact Types.
     *
     * @return array
     */
    public function contact_types()
    {
        return [
            'contact_types' => fluentcrm_contact_types(true)
        ];
    }

    /**
     * Include the sample csv url.
     *
     * @return array
     */
    public function sampleCsv()
    {
        return [
            'sampleCsv' => $this->app['url.assets'] . 'sample.csv'
        ];
    }

    public function segments()
    {
        /**
         * Determine the dynamic segments in FluentCRM.
         *
         * This filter allows you to modify the dynamic segments used in FluentCRM.
         *
         * @param array An array of dynamic segments.
         * @since 1.0.0
         *
         */
        $segments = apply_filters('fluentcrm_dynamic_segments', []);

        return [
            'segments' => $segments
        ];
    }

    public function roles()
    {
        if (!function_exists('get_editable_roles')) {
            require_once(ABSPATH . '/wp-admin/includes/user.php');
        }

        return [
            'roles' => \get_editable_roles()
        ];
    }

    public function user_roles_options()
    {
        $roles = $this->roles();

        $formattedRoles = [];
        foreach ($roles['roles'] as $role => $roleData) {
            $formattedRoles[] = [
                'id'    => $role,
                'title' => $roleData['name'],
                'slug'  => $role
            ];
        }

        return [
            'user_roles_options' => $formattedRoles
        ];
    }

    public function profile_sections()
    {
        return [
            'profile_sections' => Helper::getProfileSections()
        ];
    }

    public function custom_fields()
    {
        return [
            'custom_fields' => fluentcrm_get_option('contact_custom_fields', [])
        ];
    }

    public function getAjaxOptions(Request $request)
    {
        $optionKey = $request->getSafe('option_key');
        $search = $request->getSafe('search');
        $includedIds = $request->getSafe('values');

        $options = [];

        if ($optionKey == 'woo_categories') {
            // woocommerce categories
            if (defined('WC_PLUGIN_FILE')) {
                $cat_args = array(
                    'taxonomy'   => 'product_cat',
                    'orderby'    => 'name',
                    'order'      => 'ASC',
                    'hide_empty' => false,
                    'search'     => $search,
                    'number'     => 50
                );
                $product_categories = get_terms($cat_args);

                $pushedIds = [];
                foreach ($product_categories as $category) {
                    $options[] = [
                        'id'    => $category->term_id,
                        'title' => $category->name
                    ];
                    $pushedIds[] = $category->term_id;
                }

                if (empty($includedIds)) {
                    $includedIds = $pushedIds;
                }
                $includedIds = array_diff($includedIds, $pushedIds);

                if ($includedIds) {
                    $cat_args = array(
                        'taxonomy'   => 'product_cat',
                        'orderby'    => 'name',
                        'order'      => 'ASC',
                        'hide_empty' => false,
                        'include'    => $includedIds
                    );
                    $product_categories = get_terms($cat_args);
                    foreach ($product_categories as $category) {
                        $options[] = [
                            'id'    => $category->term_id,
                            'title' => $category->name
                        ];
                    }
                }
            }

            return [
                'options' => $options
            ];
        }

        $wooProductKeys = ['woo_products', 'product_selector_woo', 'product_selector_woo_order'];

        if (in_array($optionKey, $wooProductKeys)) {
            if (defined('WC_PLUGIN_FILE')) {

                $args = [
                    'limit'   => 50,
                    'orderby' => 'date',
                    'order'   => 'DESC',
                    's'       => $search
                ];

                $pushedIds = [];

                $subOptionKey = $request->getSafe('sub_option_key', 'sanitize_text_field', []);
                if (!empty($subOptionKey)) {
                    $args['type'] = $subOptionKey;
                }

                $products = wc_get_products($args);

                foreach ($products as $product) {
                    $productId = $product->get_id();
                    $options[] = [
                        'id'    => $productId,
                        'title' => $product->get_name()
                    ];
                    $pushedIds[] = $productId;
                }

                if (empty($includedIds)) {
                    $includedIds = $pushedIds;
                } else {
                    $includedIds = (array)$includedIds;
                }

                $includedIds = array_diff($includedIds, $pushedIds);

                if ($includedIds) {
                    $products = wc_get_products([
                        'orderby' => 'date',
                        'order'   => 'DESC',
                        'include' => $includedIds
                    ]);
                    foreach ($products as $product) {
                        $productId = $product->get_id();
                        $options[] = [
                            'id'    => $productId,
                            'title' => $product->get_name()
                        ];
                    }
                }
            }

            return [
                'options' => $options
            ];

        }

        if ($optionKey == 'edd_products' || $optionKey == 'product_selector_edd') {
            if (Helper::isEdd3() && defined('FLUENTCAMPAIGN')) {
                $options = \FluentCampaign\App\Services\Integrations\Edd\Helper::getProducts();
            }

            return [
                'options' => $options
            ];

        }

        if ($optionKey == 'voxel_products' || $optionKey == 'product_selector_voxel') {
            $pushedIds = [];
            $args = array(
                'post_type'      => 'product',
                'post_status'    => 'publish',
                'posts_per_page' => 20
            );

            if ($search) {
                $args['s'] = $search;
            }

            $query = new \WP_Query($args);
            $products = $query->posts;

            foreach ($products as $product) {
                $options[] = [
                    'id'    => $product->ID,
                    'title' => $product->post_title
                ];
                $pushedIds[] = $product->ID;
            }

            if ($includedIds) {
                $includedIds = array_diff($includedIds, $pushedIds);
                if ($includedIds) {
                    $args = array(
                        'post_type'   => 'product',
                        'post_status' => 'publish',
                        'post__in'    => $includedIds
                    );
                    $query = new \WP_Query($args);
                    $products = $query->posts;

                    foreach ($products as $product) {
                        $options[] = [
                            'id'    => $product->ID,
                            'title' => $product->post_title
                        ];
                    }
                }
            }

            return [
                'options' => $options
            ];
        }

        if ($optionKey == 'voxel_product_types') {
            $formattedTypes = [];

            if (class_exists('\Voxel\Product_Type')) {
                $product_types = \Voxel\Product_Type::get_all();

                foreach ($product_types as $product_type) {
                    $formattedTypes[] = [
                        'id'    => $product_type->get_key(),
                        'title' => $product_type->get_label()
                    ];
                }
            }

            return [
                'options' => $formattedTypes
            ];
        }

        if ($optionKey == 'campaigns' || $optionKey == 'funnels' || $optionKey == 'email_sequences') {

            if ($optionKey == 'campaigns') {
                $objectModel = Campaign::select(['id', 'title', 'status'])->where('status', '!=', 'draft');
            } else if ($optionKey == 'funnels') {
                $objectModel = Funnel::select(['id', 'title', 'status']);
            } else if ($optionKey == 'email_sequences') {
                if (!defined('FLUENTCAMPAIGN')) {
                    return [
                        'options' => []
                    ];
                }
                $objectModel = \FluentCampaign\App\Models\Sequence::select(['id', 'title', 'status']);
            } else {
                return [
                    'options' => []
                ];
            }

            $items = $objectModel
                ->when($search, function ($query) use ($search) {
                    return $query->where('title', 'LIKE', "%$search%");
                })
                ->limit(20)
                ->orderBy('id', 'DESC')
                ->get();

            $pushedIds = [];

            foreach ($items as $item) {
                $options[] = [
                    'id'    => $item->id,
                    'title' => $item->title . ' - ' . $item->id
                ];
                $pushedIds[] = $item->id;
            }

            if (!$includedIds) {
                return [
                    'options' => $options
                ];
            }

            $includedIds = (array)$includedIds;

            $includedIds = array_diff($includedIds, $pushedIds);
            if ($includedIds) {

                if ($optionKey == 'campaigns') {
                    $objectModel = Campaign::select(['id', 'title', 'status']);
                } else if ($optionKey == 'funnels') {
                    $objectModel = Funnel::select(['id', 'title', 'status']);
                } else if ($optionKey == 'email_sequences') {
                    $objectModel = \FluentCampaign\App\Models\Sequence::select(['id', 'title', 'status']);
                } else {
                    return [
                        'options' => $options
                    ];
                }

                $items = $objectModel->whereIn('id', $includedIds)->get();
                foreach ($items as $item) {
                    $options[] = [
                        'id'    => $item->id,
                        'title' => $item->title . ' - ' . $item->id
                    ];
                }
            }

            return [
                'options' => $options
            ];
        }

        if ($optionKey == 'companies') {
            if (!Helper::isCompanyEnabled()) {
                return [
                    'options' => []
                ];
            }

            $companies = Company::select(['id', 'name'])
                ->searchBy($search)
                ->limit(20)
                ->orderBy('id', 'DESC')
                ->get();

            $pushedIds = [];
            foreach ($companies as $company) {
                $options[] = [
                    'id'    => $company->id,
                    'title' => $company->name
                ];
                $pushedIds[] = $company->id;
            }

            if (empty($includedIds)) {
                $includedIds = $pushedIds;
            }
            $includedIds = array_diff($includedIds, $pushedIds);

            if ($includedIds) {
                $companies = Company::select(['id', 'name'])
                    ->whereIn('id', $includedIds)
                    ->get();
                foreach ($companies as $company) {
                    $options[] = [
                        'id'    => $company->id,
                        'title' => $company->name
                    ];
                }
            }

            return [
                'options' => $options
            ];
        }

        if ($optionKey == 'post_type') {
            // we need to verify the post type access permission here
            if (!current_user_can('edit_posts')) {
                return [
                    'options' => []
                ];
            }

            $postType = $request->getSafe('sub_option_key', 'sanitize_text_field');
            if (!$postType) {
                return [
                    'options' => []
                ];
            }

            $args = [
                'post_type'      => $postType,
                'posts_per_page' => 20
            ];

            if ($search) {
                $args['s'] = $search;
            }

            $posts = get_posts($args);

            $formattedPosts = [];
            if (!is_wp_error($posts)) {
                foreach ($posts as $post) {
                    $formattedPosts[$post->ID] = [
                        'id'    => strval($post->ID),
                        'title' => $post->post_title
                    ];
                }
            }

            if (!$includedIds) {
                return [
                    'options' => array_values($formattedPosts)
                ];
            }

            $includedIds = (array)$includedIds;

            $includedIds = array_diff($includedIds, array_keys($formattedPosts));
            if ($includedIds) {
                $posts = get_posts([
                    'post_type' => $postType,
                    'post__in'  => $includedIds
                ]);
                foreach ($posts as $post) {
                    $formattedPosts[$post->ID] = [
                        'id'    => strval($post->ID),
                        'title' => $post->post_title
                    ];
                }
            }

            return [
                'options' => array_values($formattedPosts)
            ];
        }

        if ($optionKey == 'company_industries') {
            $companyCategories = Helper::companyCategories();

            $formattedCategories = [];
            foreach ($companyCategories as $category) {
                $formattedCategories[] = [
                    'id'    => $category,
                    'title' => $category
                ];
            }

            return [
                'options' => $formattedCategories
            ];
        }

        if ($optionKey == 'company_types') {
            $companyTypes = Helper::companyTypes();

            $formattedTypes = [];
            foreach ($companyTypes as $type) {
                $formattedTypes[] = [
                    'id'    => $type,
                    'title' => $type
                ];
            }

            return [
                'options' => $formattedTypes
            ];
        }

        if ($optionKey == 'users') {

            if (!current_user_can('list_users')) {
                return [
                    'options' => []
                ];
            }

            $users = Helper::searchWPUsers($search);

            $usersWithLessFields = [];

            foreach ($users as $user) {
                $usersWithLessFields[] = [
                    'id'         => $user->ID,
                    'user_email' => $user->user_email,
                    'name'       => $user->display_name ?? $user->user_email,
                    'title'      => $user->display_name . ' (' . $user->user_email . ')'
                ];
            }

            return [
                'options' => $usersWithLessFields
            ];
        }


        return [
            /**
             * Determine the AJAX options for FluentCRM.
             *
             * This filter allows modification of the AJAX options based on the provided option key, search term, and included IDs.
             *
             * @param array  The options array to be filtered.
             * @param string $search The search term used to filter the options.
             * @param array $includedIds The IDs to be included in the options.
             * @since 2.5.9
             *
             */
            'options' => apply_filters('fluentcrm_ajax_options_' . $optionKey, [], $search, $includedIds)
        ];
    }

    public function getTaxonomyTerms(Request $request)
    {
        $taxonomy = $request->get('taxonomy');
        $search = $request->get('search');
        $includeIds = (array)$request->get('values', []);

        $args = [
            'taxonomy'   => $taxonomy,
            'hide_empty' => false,
            'number'     => 20
        ];

        if ($search) {
            $args['search'] = $search;
        }

        $terms = get_terms($args);

        $formattedTerms = [];
        if (!is_wp_error($terms)) {
            foreach ($terms as $term) {
                $formattedTerms[$term->term_id] = [
                    'id'    => strval($term->term_id),
                    'title' => $term->name
                ];
            }
        }

        if ($includeIds && $formattedTerms) {
            $includeIds = array_diff($includeIds, array_keys($formattedTerms));
            if ($includeIds) {
                $includedTerms = get_terms([
                    'taxonomy'   => $taxonomy,
                    'hide_empty' => false,
                    'include'    => $includeIds
                ]);

                if (!is_wp_error($includedTerms)) {
                    foreach ($includedTerms as $includedTerm) {
                        $formattedTerms[$includedTerm->term_id] = [
                            'id'    => strval($includedTerm->term_id),
                            'title' => $includedTerm->name
                        ];
                    }
                }
            }
        }

        return [
            'options' => array_values($formattedTerms)
        ];

    }

    public function getCascadeSelections(Request $request)
    {
        $provider = $request->get('provider');

        /**
         * Determine the cascade selection options for a given provider.
         *
         * The dynamic portion of the hook name, `$provider`, refers to the specific provider for which the options are being filtered.
         *
         * @param array {
         *     An array of options for the cascade selection.
         *
         * @type array $options The options for the selection.
         * @type bool $has_more Whether there are more options available.
         * }
         * @param array $request The request data.
         * @since 2.9.23
         *
         */
        return apply_filters('fluent_crm/cascade_selection_options_' . $provider, [
            'options'  => [],
            'has_more' => true
        ], $request->all());
    }


    /**
     * Search contacts, email campaigns (by title), automations (by title), and companies (by name).
     * Scope limits which types are queried for faster results.
     *
     * GET global-search?search=...&scope=all|subscribers|campaigns|funnels|companies
     */
    public function search()
    {
        $search = trim(sanitize_text_field($this->request->get('search', '')));
        $scope = trim(sanitize_text_field($this->request->get('scope', 'all')));
        $validScopes = ['all', 'subscribers', 'campaigns', 'funnels', 'companies', 'subscriber_notes'];
        if (!in_array($scope, $validScopes, true)) {
            $scope = 'all';
        }

        $limit = (int)apply_filters('fluent_crm/global_search_result_limit', 100);
        if ($limit <= 0) {
            $limit = 100;
        }
        global $wpdb;
        $searchLike = $search ? '%' . $wpdb->esc_like($search) . '%' : '%';

        if (empty($search)) {
            return $this->sendSuccess([
                'subscribers' => [],
                'campaigns'   => [],
                'funnels'     => []
            ]);
        }

        $canReadContacts = PermissionManager::currentUserCan('fcrm_read_contacts');
        $canReadEmails = PermissionManager::currentUserCan('fcrm_read_emails');
        $canReadFunnels = PermissionManager::currentUserCan('fcrm_read_funnels');
        $canReadCompanies = Helper::isCompanyEnabled() && PermissionManager::currentUserCan('fcrm_manage_contact_cats');

        $subscribers = [];
        $campaigns = [];
        $funnels = [];
        $companies = [];
        $subscriberNotes = [];

        if ($search !== '') {
            $querySubscribers = ($scope === 'all' || $scope === 'subscribers') && $canReadContacts;
            $queryCampaigns = ($scope === 'all' || $scope === 'campaigns') && $canReadEmails;
            $queryFunnels = ($scope === 'all' || $scope === 'funnels') && $canReadFunnels;
            $queryCompanies = ($scope === 'all' || $scope === 'companies') && $canReadCompanies;
            $queryNotes = $scope === 'subscriber_notes' && $canReadContacts;

            if ($querySubscribers) {
                $queryArgs = [
                    'with'          => [],
                    'filter_type'   => 'simple',
                    'search'        => $search,
                    'sort_by'       => 'id',
                    'sort_type'     => 'DESC',
                    'custom_fields' => false,
                    'limit'         => $limit,
                ];
                $subscribersResult = (new ContactsQuery($queryArgs))->get();
                $collection = is_array($subscribersResult) ? collect($subscribersResult) : $subscribersResult;
                $subscribers = $collection->map(function ($s) {
                    return [
                        'id'         => $s->id,
                        'email'      => $s->email,
                        'first_name' => $s->first_name ?? '',
                        'last_name'  => $s->last_name ?? '',
                        'full_name'  => $s->full_name,
                        'photo'      => $s->photo,
                    ];
                })->values()->all();
            }

            if ($queryCampaigns) {
                $campaigns = Campaign::select('id', 'title', 'status')
                    ->where('title', 'LIKE', $searchLike)
                    ->orderBy('id', 'DESC')
                    ->take($limit)
                    ->get();
            }

            if ($queryFunnels) {
                $funnels = Funnel::select('id', 'title', 'status')
                    ->where('title', 'LIKE', $searchLike)
                    ->orderBy('id', 'DESC')
                    ->take($limit)
                    ->get();
            }

            if ($queryCompanies) {
                $companies = Company::select('id', 'name', 'logo')
                    ->where('name', 'LIKE', $searchLike)
                    ->orderBy('id', 'DESC')
                    ->take($limit)
                    ->get()
                    ->map(function ($c) {
                        return [
                            'id'   => $c->id,
                            'name' => $c->name,
                            'logo' => $c->logo ?? '',
                        ];
                    })
                    ->values()
                    ->all();
            }

            if ($queryNotes) {
                $subscriberNotes = SubscriberNote::with(['subscriber' => function ($q) {
                    $q->select('id', 'email', 'first_name', 'last_name');
                }])
                    ->where(function ($q) use ($searchLike) {
                        $q->where('title', 'LIKE', $searchLike)
                            ->orWhere('description', 'LIKE', $searchLike);
                    })
                    ->orderBy('id', 'DESC')
                    ->take($limit)
                    ->get()
                    ->map(function ($note) {
                        $subscriber = $note->subscriber;
                        return [
                            'id'               => $note->id,
                            'subscriber_id'    => $note->subscriber_id,
                            'title'            => $note->title,
                            'created_at'       => $note->created_at,
                            'subscriber_name'  => $subscriber ? $subscriber->full_name : '',
                            'subscriber_email' => $subscriber ? $subscriber->email : '',
                        ];
                    })
                    ->values()
                    ->all();
            }
        }

        $response = [
            'subscribers' => $subscribers,
            'campaigns'   => $campaigns,
            'funnels'     => $funnels,
        ];
        if (Helper::isCompanyEnabled()) {
            $response['companies'] = $companies;
        }

        if ($scope === 'subscriber_notes') {
            $response['subscriber_notes'] = $subscriberNotes;
        }

        return $this->sendSuccess($response);
    }
}
