<?php

namespace NinjaTablesPro\App\Modules\DataTables\Handlers;

use NinjaTables\App\Modules\DataTables\Database\DynamicTableManager;
use NinjaTables\App\Modules\DataTables\Models\DynamicRow;
use NinjaTables\Framework\Support\Arr;

class DTFrontendEditHandler
{
    public function register()
    {
        add_action('wp_ajax_ninja_table_pro_update_row', [$this, 'handleUpdateRow'], 5);
        add_action('wp_ajax_nopriv_ninja_table_pro_update_row', [$this, 'handleUpdateRow'], 5);
        add_action('wp_ajax_ninja_table_pro_delete_row', [$this, 'handleDeleteRow'], 5);
    }

    public function handleUpdateRow()
    {
        $tableId = intval(Arr::get($_REQUEST, 'table_id'));

        if (!$this->isDataTablesRequest($tableId)) {
            return;
        }

        ninjaTablesValidateNonce('ninja_table_public_nonce');

        $rowId = false;
        if (isset($_REQUEST['row_id']) && intval($_REQUEST['row_id'])) {
            $rowId = intval($_REQUEST['row_id']);
        }

        $this->checkEditPermission($tableId, $rowId);

        $data = ninja_tables_sanitize_array($_REQUEST);

        if (!isset($data['values']) || !is_array($data['values'])) {
            $this->errorResponse(__('Invalid or missing values data', 'ninja-tables-pro'));
        }

        $values = wp_unslash($data['values']);

        if ($rowId) {
            $editPref = get_post_meta($tableId, '_ninja_table_frontedit_pref', true);
            if ($editPref && !empty($editPref['editing_items'])) {
                $editableKeys = array_keys(array_filter($editPref['editing_items'], function ($val) {
                    return $val === 'yes';
                }));
                if ($editableKeys) {
                    $values = array_intersect_key($values, array_flip($editableKeys));
                }
            }
        }

        if (!ninja_table_admin_role()) {
            $values = ninja_tables_sanitize_frontend_values($values);
        }

        $this->validateRequiredFields($tableId, $values);

        $dynamicRow = new DynamicRow($tableId);

        if (!$dynamicRow->tableExists()) {
            $this->errorResponse(__('Table not found', 'ninja-tables-pro'));
        }

        if ($rowId) {
            $existingRow = $dynamicRow->find($rowId);
            if (!$existingRow) {
                $this->errorResponse(__('No record found to update, Please try again', 'ninja-tables-pro'));
            }

            $mapped       = $dynamicRow->mapRowToUserKeys($existingRow);
            $mergedValues = $mapped['values'];
            foreach ($values as $key => $value) {
                $mergedValues[$key] = $value;
            }

            $dynamicRow->update($rowId, $mergedValues);
            $responseValues = $mergedValues;
        } else {
            $columns    = ninja_table_get_table_columns($tableId, 'admin');
            $valuePairs = [];
            foreach ($columns as $column) {
                $columnKey              = $column['key'];
                $valuePairs[$columnKey] = isset($values[$columnKey]) ? $values[$columnKey] : '';
            }

            $rowId = $dynamicRow->insert($valuePairs);

            if (!$rowId) {
                $this->errorResponse(__('Failed to save data', 'ninja-tables-pro'));
            }

            $responseValues = $valuePairs;
        }

        update_post_meta($tableId, '_last_edited_time', date('Y-m-d H:i:s'));
        if ($userId = get_current_user_id()) {
            update_post_meta($tableId, '_last_edited_by', $userId);
        }

        wp_send_json_success([
            'message' => __('Data successfully saved', 'ninja-tables-pro'),
            'values'  => [
                'options' => [
                    'classes' => 'nt_row_id_' . $rowId,
                ],
                'value' => $responseValues,
            ],
        ], 200);
    }

    public function handleDeleteRow()
    {
        $tableId = intval(Arr::get($_POST, 'table_id'));

        if (!$this->isDataTablesRequest($tableId)) {
            return;
        }

        ninjaTablesValidateNonce('ninja_table_public_nonce');

        $rowId = intval(Arr::get($_POST, 'row_id'));
        if (!$rowId) {
            $this->errorResponse(__('Invalid row ID', 'ninja-tables-pro'));
        }

        $this->checkDeletePermission($tableId, $rowId);

        $dynamicRow = new DynamicRow($tableId);
        $dynamicRow->delete($rowId);

        update_post_meta($tableId, '_last_edited_time', date('Y-m-d H:i:s'));
        if ($userId = get_current_user_id()) {
            update_post_meta($tableId, '_last_edited_by', $userId);
        }

        wp_send_json_success([
            'message' => __('Data successfully deleted', 'ninja-tables-pro'),
        ], 200);
    }

    private function isDataTablesRequest($tableId)
    {
        if (!$tableId) {
            return false;
        }

        $settings = ninja_table_get_table_settings($tableId, 'public');

        return isset($settings['library']) && $settings['library'] === 'datatables';
    }

    private function checkEditPermission($tableId, $rowId = false)
    {
        if (current_user_can('administrator')) {
            return;
        }

        $operation = $rowId ? 'update' : 'add';
        $userId    = get_current_user_id();
        $settings  = get_post_meta($tableId, '_ninja_table_frontedit_settings', true);

        if (!$userId && $operation === 'update') {
            $this->errorResponse(__('Sorry! You do not have permission to edit this data', 'ninja-tables-pro'));
        }

        if (!$this->hasEditingPermission($tableId, $userId, $settings)) {
            $this->errorResponse(__('Sorry! You do not have permission to edit this data', 'ninja-tables-pro'));
        }

        if (Arr::get($settings, 'own_data_only') === 'no' || $operation === 'add') {
            return;
        }

        $dynamicRow = new DynamicRow($tableId);
        $row        = $dynamicRow->find($rowId);
        $colOwnerId = DynamicTableManager::COL_OWNER_ID;

        if ($row && $userId > 0 && (int)$row->{$colOwnerId} === (int)$userId) {
            return;
        }

        $this->errorResponse(__('Sorry! You do not have permission to perform this action', 'ninja-tables-pro'));
    }

    private function checkDeletePermission($tableId, $rowId)
    {
        if (current_user_can('administrator')) {
            return;
        }

        $userId   = get_current_user_id();
        $settings = get_post_meta($tableId, '_ninja_table_frontedit_settings', true);

        if (!$this->hasDeletePermission($tableId, $userId, $settings)) {
            $this->errorResponse(__('Sorry! You do not have permission to edit this data', 'ninja-tables-pro'));
        }

        if (Arr::get($settings, 'own_data_only') === 'no') {
            return;
        }

        $dynamicRow = new DynamicRow($tableId);
        $row        = $dynamicRow->find($rowId);
        $colOwnerId = DynamicTableManager::COL_OWNER_ID;

        if ($row && $userId > 0 && (int)$row->{$colOwnerId} === (int)$userId) {
            return;
        }

        $this->errorResponse(__('Sorry! You do not have permission to perform this action', 'ninja-tables-pro'));
    }

    private function hasEditingPermission($tableId, $userId = false, $settings = false)
    {
        if (current_user_can('administrator')) {
            return true;
        }

        if (!$settings) {
            $settings = get_post_meta($tableId, '_ninja_table_frontedit_settings', true);
        }

        if (Arr::get($settings, 'allow_frontend') !== 'yes' || !Arr::get($settings, 'user_roles_editing')) {
            return false;
        }

        $editingRoles = Arr::get($settings, 'user_roles_editing');
        if (!$editingRoles) {
            return false;
        }

        foreach ($editingRoles as $role) {
            if (current_user_can($role)) {
                return true;
            }
        }

        if (!$userId && in_array('__public_users__', $editingRoles)) {
            return true;
        }

        return false;
    }

    private function hasDeletePermission($tableId, $userId = false, $settings = false)
    {
        if (!$userId) {
            return false;
        }

        if (!$settings) {
            $settings = get_post_meta($tableId, '_ninja_table_frontedit_settings', true);
        }

        if (!$settings || !Arr::get($settings, 'user_roles_deleting')) {
            return false;
        }

        $deletingRoles = Arr::get($settings, 'user_roles_deleting');

        foreach ($deletingRoles as $role) {
            if (current_user_can($role)) {
                return true;
            }
        }

        return false;
    }

    private function validateRequiredFields($tableId, $values)
    {
        $editPref = get_post_meta($tableId, '_ninja_table_frontedit_pref', true);
        if (!$editPref || empty($editPref['required_items'])) {
            return;
        }

        $requiredFields = array_keys(array_filter($editPref['required_items'], function ($val) {
            return $val === 'yes';
        }));

        $editingFields = array_keys(array_filter(
            Arr::get($editPref, 'editing_items', []),
            function ($val) {
                return $val === 'yes';
            }
        ));

        $requiredFields = array_intersect($requiredFields, $editingFields);

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

        $columns        = ninja_table_get_table_columns($tableId, 'admin');
        $allColumnArray = [];
        foreach ($columns as $column) {
            $allColumnArray[$column['key']] = $column['name'];
        }

        $validRequiredFields = array_intersect(array_keys($allColumnArray), $requiredFields);

        $errors = [];
        foreach ($validRequiredFields as $requiredField) {
            if (!isset($values[$requiredField]) || $values[$requiredField] === '') {
                $errors[$requiredField] = $allColumnArray[$requiredField] . __(' is required', 'ninja-tables-pro');
            }
        }

        if ($errors) {
            wp_send_json_error([
                'errors'  => $errors,
                'message' => __('Validation failed, Please fill up required fields', 'ninja-tables-pro'),
            ], 400);
        }
    }

    private function errorResponse($message, $code = 400)
    {
        wp_send_json_error([
            'message' => $message,
        ], $code);
    }
}
