<?php

namespace BookneticApp\Backend\Appointments\Helpers;

use BookneticApp\Config;
use BookneticApp\Models\Appointment;
use BookneticApp\Models\Customer;
use BookneticApp\Models\Workflow;
use BookneticApp\Providers\Core\Permission;
use BookneticApp\Providers\Helpers\Date;
use BookneticApp\Providers\Helpers\Helper;
use BookneticSaaS\Models\Tenant;

class ReminderService
{
    private static function customerBirthday($time, $offset, $genderFilter, $yearFilter, $monthFilter, $workflow)
    {
        $startDate = Date::dateTimeSQL($time);
        $now = Date::dateTimeSQL();

        $startTime = Date::dateTimeSQL($startDate, '-1 minutes');
        $endTime = Date::dateTimeSQL($startDate, '+29 minutes');

        if (Date::epoch($now) < Date::epoch($startTime) || Date::epoch($now) > Date::epoch($endTime)) {
            return false;
        }

        $now = Date::dateTimeSQL($now, "+{$offset} seconds");

        $nearbyCustomerQuery = Customer::where(
            'Day(birthdate)',
            '=',
            (int)Date::format('d', $now)
        )
            ->where(
                'MONTH(birthdate)',
                '=',
                (int)Date::format('m', $now)
            );

        if ($genderFilter !== null && (in_array('male', $genderFilter) || in_array('female', $genderFilter))) {
            $nearbyCustomerQuery->where('gender', 'in', $genderFilter);
        } elseif (in_array('not_specified', $genderFilter)) {
            $nearbyCustomerQuery->whereIsNull('gender');
        }

        if (is_array($yearFilter) && !in_array('-', $yearFilter) && $yearFilter != null) {
            $nearbyCustomerQuery->where('YEAR(birthdate)', 'in', $yearFilter);
        }
        if (is_array($monthFilter) && !in_array(0, $monthFilter) && $monthFilter != null) {
            $nearbyCustomerQuery->where('MONTH(birthdate)', 'in', $monthFilter);
        }

        $nearbyCustomers = $nearbyCustomerQuery->fetchAll();

        if (empty($nearbyCustomers)) {
            return false;
        }

        $workflowActions = $workflow->workflow_actions()->where('is_active', true)->fetchAll();

        foreach ($nearbyCustomers as $nearbyCustomer) {
            $triggeredCustomerBirthdateWorkflow = Customer::getData(
                $nearbyCustomer->id,
                'triggered_customer_birthday_workflow_' . Date::format('Y'),
                false,
            );

            if ($triggeredCustomerBirthdateWorkflow) {
                continue;
            }

            $params = [
                'customer_id' => $nearbyCustomer->id,
            ];

            foreach ($workflowActions as $action) {
                $driver = Config::getWorkflowDriversManager()->get($action['driver']);
                if (!empty($driver)) {
                    $driver->handle($params, $action, Config::getShortCodeService());
                }
            }

            Customer::setData(
                $nearbyCustomer->id,
                'triggered_customer_birthday_workflow_' . Date::format('Y'),
                true,
                false
            );
        }
        return true;
    }

    public static function run(): void
    {
        set_time_limit(0);
        self::triggerWorkflows();
    }

    public static function triggerWorkflows(): void
    {
        $workflowActions = [
            'booking_starts',
            'booking_ends',
            'customer_birthday'
        ];

        if (Helper::isSaaSVersion()) {
            $workflowActions[] = 'tenant_notified';
        }

        $backUpTenantId = Permission::tenantId();
        $workflows = Workflow::noTenant()->where('`when`', $workflowActions)
            ->where('is_active', true)
            ->fetchAll();

        foreach ($workflows as $workflow) {
            Permission::setTenantId($workflow->tenant_id);

            $offset = 0; // in minutes
            $status_filter = [];
            $locations_filter = [];
            $service_filter = [];
            $staff_filter = [];
            $gender_filter = [];
            $year_filter = [];
            $month_filter = [];
            $time = [];
            $locale = null;

            try {
                if (!empty($workflow->data)) {
                    $data = json_decode($workflow->data, true);
                    $offset = json_decode($data['offset_value'], true) * 60;
                    $offset *= json_decode($data['offset_sign'], true) === 'before' ? 1 : -1;

                    if (!isset($data['offset_type'])) {
                        $data['offset_type'] = 'day';
                    }

                    if ($data['offset_type'] === 'hour') {
                        $offset *= 60;
                    }
                    if ($data['offset_type'] === 'day') {
                        $offset *= 60 * 24;
                    }

                    $status_filter = $data['statuses'] ?? [];
                    $locations_filter = $data['locations'] ?? [];
                    $service_filter = $data['services'] ?? [];
                    $staff_filter = $data['staffs'] ?? [];
                    $gender_filter = $data['gender'] ?? [];
                    $year_filter = $data['years'] ?? null;
                    $month_filter = $data['month'] ?? null;
                    $time = json_decode($data['input_time']);
                    $locale = $data['locale'] ?? null;
                }
            } catch (\Exception $e) {
            }

            if ($workflow->when === 'tenant_notified') {
                $tenants = Tenant::where('expires_in', '>=', date('Y-m-d', Date::epoch('now', '-5 minutes') + $offset))
                    ->where('expires_in', '<=', date('Y-m-d', Date::epoch('now', '+5 minutes') + $offset))
                    ->fetchAll();

                foreach ($tenants as $tenant) {
                    $alreadyTriggeredWorkflowIDs = json_decode(Tenant::getData($tenant->id, 'triggered_cronjob_workflows', '[]'), true);

                    if (!in_array($workflow->id, $alreadyTriggeredWorkflowIDs)) {
                        do_action('bkntcsaas_tenant_notified', $tenant->id);

                        $alreadyTriggeredWorkflowIDs[] = $workflow->id;

                        Tenant::setData($tenant->id, 'triggered_cronjob_workflows', json_encode($alreadyTriggeredWorkflowIDs), count($alreadyTriggeredWorkflowIDs));
                    }
                }
            } elseif ($workflow->when === 'customer_birthday') {
                self::customerBirthday($time, $offset, $gender_filter, $year_filter, $month_filter, $workflow);
            } else {
                if ($workflow->when === 'booking_ends') {
                    $nearbyAppointments = Appointment::where('ends_at', '>=', Date::epoch('now', '-5 minutes') + $offset)
                        ->where('ends_at', '<=', Date::epoch('now', '+5 minutes') + $offset);
                } else {
                    $nearbyAppointments = Appointment::where('starts_at', '>=', Date::epoch('now', '-5 minutes') + $offset)
                        ->where('starts_at', '<=', Date::epoch('now', '+5 minutes') + $offset);
                }

                if (is_array($locations_filter) && count($locations_filter) > 0) {
                    $nearbyAppointments->where('location_id', $locations_filter);
                }

                if (is_array($service_filter) && count($service_filter) > 0) {
                    $nearbyAppointments->where('service_id', $service_filter);
                }

                if (is_array($staff_filter) && count($staff_filter) > 0) {
                    $nearbyAppointments->where('staff_id', $staff_filter);
                }

                if ($locale) {
                    $nearbyAppointments->where('locale', $locale);
                }

                if (is_array($status_filter) && count($status_filter) > 0) {
                    $nearbyAppointments->where('status', $status_filter);
                }

                $nearbyAppointments = $nearbyAppointments->fetchAll();

                $workflow_actions = $workflow->workflow_actions()->where('is_active', true)->fetchAll();

                foreach ($nearbyAppointments as $nearbyAppointment) {
                    $alreadyTriggeredWorkflowIDs = json_decode(Appointment::getData($nearbyAppointment->id, 'triggered_cronjob_workflows', '[]'), true);
                    if (in_array($workflow->id, $alreadyTriggeredWorkflowIDs)) {
                        continue;
                    }

                    $params = [
                        'appointment_id' => $nearbyAppointment->id,
                        'customer_id' => $nearbyAppointment->customer_id,
                        'staff_id' => $nearbyAppointment->staff_id,
                        'location_id' => $nearbyAppointment->location_id,
                        'service_id' => $nearbyAppointment->service_id
                    ];

                    foreach ($workflow_actions as $action) {
                        $driver = Config::getWorkflowDriversManager()->get($action['driver']);
                        if (!empty($driver)) {
                            $action->when = $workflow->when;
                            $driver->handle($params, $action, Config::getShortCodeService());
                        }
                    }

                    $alreadyTriggeredWorkflowIDs[] = $workflow->id;
                    Appointment::setData($nearbyAppointment->id, 'triggered_cronjob_workflows', json_encode($alreadyTriggeredWorkflowIDs), count($alreadyTriggeredWorkflowIDs) > 1);
                }
            }
        }

        Permission::setTenantId($backUpTenantId);
    }
}
