<?php

namespace BitApps\PiPro\src\Integrations\FluentCrm;

use BitApps\Pi\Helpers\Utility;
use BitApps\Pi\src\Flow\NodeInfoProvider;
use BitApps\PiPro\src\Integrations\FluentCrm\helpers\FluentCrmActionHelper;
use FluentCrm\App\Models\Campaign;
use FluentCrm\App\Models\Company;
use FluentCrm\App\Models\Lists;
use FluentCrm\App\Models\SubscriberNote;
use FluentCrm\App\Models\Tag;
use FluentCrm\App\Services\Sanitize;

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


final class FluentCrmServices
{
    private $nodeInfoProvider;

    public function __construct(NodeInfoProvider $nodeInfoProvider)
    {
        $this->nodeInfoProvider = $nodeInfoProvider;
    }

    /**
     * Create contact.
     *
     * @return collection
     */
    public function createContact()
    {
        $fields = $this->nodeInfoProvider->getFieldMapRepeaters('field-map.value', false, true, 'fluentCrmField', 'value');
        $customFields = $this->nodeInfoProvider->getFieldMapRepeaters('custom-field-map.value', false, true, 'fluentCrmField', 'value');

        $lists = $this->nodeInfoProvider->getFieldMapConfigs('list-id.value');
        $tags = $this->nodeInfoProvider->getFieldMapConfigs('tag-id.value');
        $fields['company_id'] = $this->nodeInfoProvider->getFieldMapConfigs('company-id.value');

        $fields = array_merge($fields, $customFields);
        $payload = array_merge(
            $fields,
            [
                'list_ids' => $lists,
                'tag_ids'  => $tags
            ]
        );

        $rules = ['email' => ['required', 'email']];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($fields, $rules, $payload, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $contact = FluentCrmApi('contacts')->createOrUpdate($fields);

        if ($contact->status === 'pending') {
            $contact->sendDoubleOptinEmail();
        }

        if (!empty($lists)) {
            $contact->attachLists($lists);
        }

        if (!empty($tags)) {
            $contact->attachTags($tags);
        }

        return [
            'response'    => $contact->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Get All Contact.
     *
     * @return collection
     */
    public function getAllContact()
    {
        return FluentCrmActionHelper::getAllData('contacts');
    }

    /**
     * Get Contact by id.
     *
     * @return collection
     */
    public function getContactById()
    {
        $id = $this->nodeInfoProvider->getFieldMapData()['id'] ?? null;

        $payload = ['id' => $id];

        $rules = ['id' => ['required']];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        return [
            'response'    => FluentCrmActionHelper::getContact($id),
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Get Contact by email.
     *
     * @return collection
     */
    public function getContactByEmail()
    {
        $email = $this->nodeInfoProvider->getFieldMapData()['email'] ?? null;

        $payload = ['email' => $email];
        $rules = ['email' => ['required', 'email']];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        return [
            'response'    => FluentCrmActionHelper::getContact($email),
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Get Contact by user id.
     *
     * @return collection
     */
    public function getContactByUserId()
    {
        $userId = $this->nodeInfoProvider->getFieldMapData()['userId'] ?? null;

        $rules = ['user_id' => ['required']];
        $payload = ['user_id' => $userId];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $contactApi = FluentCrmApi('contacts');

        $contacts = $contactApi->getContactByUserRef($userId);

        return [
            'response'    => $contacts->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Get Contact by tag ids.
     *
     * @return collection
     */
    public function getContactByTagIds()
    {
        $tagIds = $this->nodeInfoProvider->getFieldMapData()['tagIds'] ?? null;
        $tagIds = Utility::convertStringToArray($tagIds);

        $rules = ['tag_ids' => ['required']];
        $payload = ['tag_ids' => $tagIds];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $contactApi = FluentCrmApi('contacts');

        $contacts = $contactApi->getInstance()->filterByTags($tagIds)->get();

        return [
            'response'    => $contacts->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Get Contact by list ids.
     *
     * @return collection
     */
    public function getContactByListIds()
    {
        $listIds = $this->nodeInfoProvider->getFieldMapData()['listIds'] ?? null;
        $listIds = Utility::convertStringToArray($listIds);

        $rules = ['list_ids' => ['required']];
        $payload = ['list_ids' => $listIds];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $contactApi = FluentCrmApi('contacts');

        $contacts = $contactApi->getInstance()->filterByLists($listIds)->get();

        return [
            'response'    => $contacts->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Get Contact by status.
     *
     * @return collection
     */
    public function getContactByStatus()
    {
        $status = $this->nodeInfoProvider->getFieldMapData()['status'] ?? null;

        $rules = ['status' => ['required']];
        $payload = ['status' => $status];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $contactApi = FluentCrmApi('contacts');

        $contacts = $contactApi->getInstance()->whereIn('status', [$status])->get();

        return [
            'response'    => $contacts->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Get Current Contact.
     *
     * @return collection
     */
    public function getCurrentContact()
    {
        $contact = null;

        if (\function_exists('FluentCrmApi')) {
            $contactApi = FluentCrmApi('contacts');

            $contact = $contactApi->getCurrentContact(true, true);
        }

        return [
            'response'    => $contact->toArray() ?? [],
            'payload'     => [],
            'status_code' => 200
        ];
    }

    /**
     * Get All Tag.
     *
     * @return collection
     */
    public function getAllTag()
    {
        return FluentCrmActionHelper::getAllData('tags');
    }

    /**
     * Create new tag.
     *
     * @return collection
     */
    public function createTag()
    {
        $fields = $this->nodeInfoProvider->getFieldMapData();

        $title = $fields['title'] ?? null;
        $slug = $fields['slug'] ?? null;
        $description = $fields['description'] ?? null;

        $rules = ['title' => ['required']];
        $payload = ['title' => $title, 'slug' => $slug, 'description' => $description];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, false, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $tag = Tag::updateOrCreate(
            [
                'title'       => $title,
                'slug'        => $slug,
                'description' => $description
            ],
            [
                'slug' => $slug
            ]
        );

        return [
            'response'    => $tag->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Add or remove tag to/from contact.
     *
     * @param bool $isAdding
     *
     * @return collection
     */
    public function handleTagContactAction($isAdding = true)
    {
        $tagIds = $this->nodeInfoProvider->getFieldMapConfigs('tag-id.value');
        $contactId = $this->nodeInfoProvider->getFieldMapData()['contactId'] ?? null;

        $payload = ['tag_ids' => $tagIds, 'contact_id' => $contactId];
        $rules = ['tag_ids' => ['required'], 'contact_id' => ['required']];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $contactApi = FluentCrmApi('contacts');
        $contact = $contactApi->getContact($contactId);

        if (empty($contact)) {
            return [
                'response'    => __('Contact not found', 'bit-pi'),
                'payload'     => $payload,
                'status_code' => 200
            ];
        }

        if ($isAdding) {
            $contact->attachTags($tagIds);
        } else {
            $contact->detachTags($tagIds);
        }

        return [
            'response'    => $contact->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Add or remove List to/from contact.
     *
     * @param bool $isAdding
     *
     * @return collection
     */
    public function handleListContactAction($isAdding = true)
    {
        $listIds = $this->nodeInfoProvider->getFieldMapConfigs('list-id.value');
        $contactId = $this->nodeInfoProvider->getFieldMapData()['contactId'] ?? null;

        $payload = ['list_ids' => $listIds, 'contact_id' => $contactId];
        $rules = ['list_ids' => ['required'], 'contact_id' => ['required']];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $contactApi = FluentCrmApi('contacts');
        $contact = $contactApi->getContact($contactId);

        if (empty($contact)) {
            return [
                'response'    => __('Contact not found', 'bit-pi'),
                'payload'     => $payload,
                'status_code' => 200
            ];
        }

        if ($isAdding) {
            $contact->attachLists($listIds);
        } else {
            $contact->detachLists($listIds);
        }

        return [
            'response'    => $contact->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Get All List.
     *
     * @return collection
     */
    public function getAllList()
    {
        return FluentCrmActionHelper::getAllData('lists');
    }

    /**
     * Create new tag.
     *
     * @return collection
     */
    public function createList()
    {
        $fields = $this->nodeInfoProvider->getFieldMapData();

        $title = $fields['title'] ?? null;
        $slug = $fields['slug'] ?? null;
        $description = $fields['description'] ?? null;

        $payload = ['title' => $title, 'slug' => $slug, 'description' => $description];
        $rules = ['title' => ['required']];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, false, false, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $list = Lists::updateOrCreate(
            [
                'title'       => $title,
                'slug'        => $slug,
                'description' => $description
            ],
            [
                'slug' => $slug
            ]
        );

        return [
            'response'    => $list->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Get All Company.
     *
     * @return collection
     */
    public function getAllCompany()
    {
        return [
            'response'    => FluentCrmActionHelper::getAllCompany(),
            'payload'     => [],
            'status_code' => 200
        ];
    }

    /**
     * Get Company by id.
     *
     * @return collection
     */
    public function getCompanyById()
    {
        $id = $this->nodeInfoProvider->getFieldMapData()['id'] ?? null;

        $payload = ['id' => $id];
        $rules = ['id' => ['required']];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $company = [];

        if (FluentCrmActionHelper::isCompanyExists()) {
            $company = Company::where('id', $id)->first();
            $company = $company->toArray() ?? [];
        }

        return [
            'response'    => $company,
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Get Company by id.
     *
     * @return collection
     */
    public function createCompany()
    {
        $payload = $this->nodeInfoProvider->getFieldMapRepeaters('field-map.value', false, true, 'fluentCrmField', 'value');
        $payload['custom_values'] = $this->nodeInfoProvider->getFieldMapRepeaters('custom-field-map.value', false, true, 'fluentCrmField', 'value');

        $rules = ['name' => ['required']];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, true, false, false, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $company = FluentCrmApi('companies')->createOrUpdate($payload);

        return [
            'response'    => $company->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Add or remove company to/from contact.
     *
     * @param bool $isAdding
     *
     * @return collection
     */
    public function handleCompanyContactAction($isAdding = true)
    {
        $companyIds = $this->nodeInfoProvider->getFieldMapConfigs('company-id.value');
        $contactId = $this->nodeInfoProvider->getFieldMapData()['contactId'] ?? null;

        $payload = ['company_ids' => $companyIds, 'contact_id' => $contactId];
        $rules = ['company_ids' => ['required'], 'contact_id' => ['required']];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $contactApi = FluentCrmApi('contacts');
        $contact = $contactApi->getContact($contactId);

        if (empty($contact)) {
            return [
                'response'    => __('Contact not found', 'bit-pi'),
                'payload'     => $payload,
                'status_code' => 200
            ];
        }

        if ($isAdding) {
            $contact->attachCompanies($companyIds);
        } else {
            $contact->detachCompanies($companyIds);
        }

        return [
            'response'    => $contact->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Add event.
     *
     * @return collection
     */
    public function addEvent()
    {
        $payload = $this->nodeInfoProvider->getFieldMapData();

        $rules = [
            'email'    => ['required', 'email'],
            'title'    => ['required'],
            'eventKey' => ['required']
        ];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        $event = FluentCrmApi('event_tracker')->track(
            [
                'event_key' => $payload['eventKey'] ?? null,
                'title'     => $payload['title'] ?? null,
                'value'     => $payload['value'] ?? null,
                'email'     => $payload['email'] ?? null,
                'provider'  => $payload['provider'] ?? null,
            ],
            true
        );

        return [
            'response'    => $event->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Add note.
     *
     * @return collection
     */
    public function addNote()
    {
        $payload = $this->nodeInfoProvider->getFieldMapData();

        $rules = ['title' => ['required']];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($payload, $rules, null, true);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        if (!class_exists('FluentCrm\App\Models\SubscriberNote') || !class_exists('FluentCrm\App\Services\Sanitize')) {
            return [
                'response'    => __('SubscriberNote model is not available', 'bit-pi'),
                'payload'     => $payload,
                'status_code' => 200
            ];
        }

        $sanitizeNote = Sanitize::contactNote(
            [
                'title'         => $payload['title'] ?? null,
                'description'   => $payload['description'] ?? null,
                'type'          => $payload['type'] ?? null,
                'created_at'    => current_time('mysql'),
                'subscriber_id' => $payload['contactId'] ?? null,
            ]
        );

        $note = SubscriberNote::create(wp_unslash((array) $sanitizeNote));

        return [
            'response'    => $note->toArray() ?? [],
            'payload'     => $payload,
            'status_code' => 200
        ];
    }

    /**
     * Get All Campaign.
     *
     * @return collection
     */
    public function getAllCampaign()
    {
        if ($error = $this->checkCampaignModelExist()) {
            return $error;
        }

        $campaigns = Campaign::all();
        $data = ['campaigns' => method_exists($campaigns, 'toArray') ? $campaigns->toArray() : []];

        return [
            'response'    => $data,
            'payload'     => [],
            'status_code' => 200
        ];
    }

    /**
     * Get Campaign by id.
     *
     * @return collection
     */
    public function getCampaignById()
    {
        $fieldMappings = $this->nodeInfoProvider->getFieldMapData();

        $rules = ['id' => ['required']];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($fieldMappings, $rules);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        if ($error = $this->checkCampaignModelExist($fieldMappings)) {
            return $error;
        }

        $campaign = Campaign::find($fieldMappings['id']);
        $data = ['campaign' => $campaign ? $campaign->toArray() : []];

        return [
            'response'    => $data,
            'payload'     => $fieldMappings,
            'status_code' => 200
        ];
    }

    /**
     * Create Campaign.
     *
     * @return collection
     */
    public function createCampaign()
    {
        $fieldMappings = $this->nodeInfoProvider->getFieldMapData();

        $rules = [
            'title' => ['required', 'sanitize:text']
        ];

        $validationErrors = FluentCrmActionHelper::validateFieldMap($fieldMappings, $rules);

        if (!empty($validationErrors)) {
            return $validationErrors;
        }

        if ($error = $this->checkCampaignModelExist($fieldMappings)) {
            return $error;
        }

        $campaign = Campaign::create($fieldMappings);
        $data = ['campaign' => $campaign ? $campaign->toArray() : []];

        return [
            'response'    => $data,
            'payload'     => $fieldMappings,
            'status_code' => 200
        ];
    }

    private function checkCampaignModelExist($payload = [])
    {
        if (!class_exists('\FluentCrm\App\Models\Campaign')) {
            return [
                'response'    => __('Campaign model is not available', 'bit-pi'),
                'payload'     => $payload,
                'status_code' => 400
            ];
        }
    }
}
