<?php

namespace SEOAIC;

use DateInterval;
use DateTimeImmutable;
use DateTime;
use SeoaicAjaxValidation;
use SEOAIC\repositories\PostRepository;

use function PHPSTORM_META\type;

class SoaicSEA {
    private $seoaic;
    private $postsRepository;

    function __construct($seoaic) {
        $this->seoaic = $seoaic;
        $this->postsRepository = new PostRepository();

        add_action('wp_ajax_seoaic_get_sea_campaigns', [$this, 'getSEACampaigns']);
        add_action('wp_ajax_seoaic_sea_athorize', [$this, 'SEAAuthorize']);
        add_action('wp_ajax_seoaic_sea_edit_campaign', [$this, 'SEAEditCampaign']);
        add_action('wp_ajax_seoaic_sea_ad_copy', [$this, 'ajaxSEAAdCopy']);
        add_action('wp_ajax_seoaic_sea_create_ad_copy', [$this, 'ajaxSEACreateAdCopy']);
        add_action('wp_ajax_seoaic_campaign_change_status', [$this, 'SEAChangeStatus']);
        add_action('wp_ajax_seoaic_sea_ad_group_change_status', [$this, 'SEAAdGroupChangeStatus']);
        add_action('wp_ajax_seoaic_sea_get_performance', [$this, 'SEAGetPerformance']);
        add_action('wp_ajax_seoaic_sea_get_campaigns_stats', [$this, 'SEAGetCampaignStats']);
        add_action('wp_ajax_seoaic_sea_create_campaign', [$this, 'SEAcreateCampaign']);
        add_action('wp_ajax_seoaic_sea_get_campaign', [$this, 'SEAGetCampaignByPostId']);
        add_action('wp_ajax_seoaic_get_sea_accounts', [$this, 'SEAGetAccounts']);
        add_action('wp_ajax_seoaic_sea_set_account', [$this, 'SEASetAccount']);
        add_action('wp_ajax_seoaic_get_sea_campaign_group', [$this, 'SEAGetCampaignGroup']);
        add_action('wp_ajax_seoaic_sea_log_out', [$this, 'SEALogOut']);
        add_action('wp_ajax_seoaic_sea_get_currency_symbol', [$this, 'getSEACurrencySymbol']);
        add_action('wp_ajax_seoaic_sea_set_settings', [$this, 'SEASetSettings']);
        add_action('wp_ajax_seoaic_sea_get_settings', [$this, 'SEAGetSettings']);
        add_action('wp_ajax_seoaic_sea_search_locations', [$this, 'SEASearchLocations']);
        add_action('wp_ajax_seoaic_sea_refresh_ad_group', [$this, 'SEARefreshAdGroup']);
    }

    public function SEAInintCurl($url, $method = 'post', $data = [], $checkMeta = true) {
        global $SEOAIC;

        $seaToken = $this->getSeaAuthToken();

        if ($checkMeta && empty($seaToken['token'])) {
            $this->SEAUnauthorized();
        }

        if ($method === 'get') {
            $result = $SEOAIC->curl->setMethodGet()->initWithReturn($url, $data, false, true);
        } elseif ($method === 'post') {
            $result = $SEOAIC->curl->setMethodPost()->initWithReturn($url, wp_json_encode($data), false, true);
        }

        $_request = wp_unslash($_REQUEST);

        if (!empty($result['response_code']) && intval($result['response_code']) === 404 && $_request['action'] !== 'seoaic_get_sea_campaigns') {
            $this->SEAUnauthorized();
        }

        if ((!empty($result['status'] && $result['status'] === 'error')) || (!empty($result['response_code']) && intval($result['response_code']) === 400)) {
           SEOAICAjaxResponse::alert($result['message'])->wpSend();
        }

        if (!empty($result['response_code']) && intval($result['response_code']) === 443) {
            $message = sprintf(
                __('Google Ads account is not selected. Please choose an account to continue: <a href="%s">Select Account</a>', 'seoaic'),
                esc_url(admin_url('admin.php?page=seoaic-sea-select-account'))
            );

            $result['message'] = $message;
        }

        $result['is_owner'] = !empty($seaToken['is_owner']) ? $seaToken['is_owner'] : false;

        return $result;
    }

    // This function retrieves the SEA campaigns for the user.
    public function getSEACampaigns() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $result = $this->SEAInintCurl('/api/sea/campaigns', 'get', [], false);

        $associativePosts = $this->postsRepository->get_associative_posts_array(true, 'id');
        $result['associativePosts'] = $associativePosts;

        SEOAICAjaxResponse::success()->addFields($result ?? [])->wpSend();
    }

    // This function retrieves the SEA currency symbol.
    public function getSEACurrencySymbol() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $result = $this->SEAInintCurl('/api/sea/accounts/currency-code', 'get', [], false);

        SEOAICAjaxResponse::success()->addFields($result ?? [])->wpSend();
    }

    // This function handles the authorization for SEA using Google.
    public function SEAAuthorize() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $data = [
            'redirect_url' => admin_url('admin.php?page=seoaic-sea-auth-success'),
            'blog_user_id' => get_current_user_id(),
        ];

        $result = $this->SEAInintCurl('/api/sea/auth/google', 'get', $data, false);

        if ($result['status'] === 'success') {
            SEOAICAjaxResponse::redirect()->redirectTo($result['data']['authRedirectTo'], true)->wpSend();
        }

        SEOAICAjaxResponse::success()->wpSend();
    }

    public function ajaxSEAAdCopy() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $campaignId = !empty($_post['campaignId']) ? $_post['campaignId'] : '';

        $groups = null;
        if (isset($_post['groups'])) {
            $groupsRaw = $_post['groups'];
            if (is_string($groupsRaw)) {
                $decoded = json_decode($groupsRaw, true);
                if (json_last_error() === JSON_ERROR_NONE) {
                    $groups = $decoded;
                }
            } elseif (is_array($groupsRaw)) {
                $groups = $groupsRaw;
            }
        }

        if (empty($groups)) {
            $groupId      = !empty($_post['groupId']) ? $_post['groupId'] : 0;
            $adId         = !empty($_post['adId']) ? $_post['adId'] : 0;
            $titles       = (isset($_post['titles']) && is_array($_post['titles'])) ? $this->sanitize_string_array($_post['titles']) : [];
            $descriptions = (isset($_post['descriptions']) && is_array($_post['descriptions'])) ? $this->sanitize_string_array($_post['descriptions']) : [];

            if ($groupId === '' || $adId === '') {
                SEOAICAjaxResponse::alert(esc_html__('Invalid group or ad ID.', 'seoaic'))->wpSend();
            }
            if (empty($titles) && empty($descriptions)) {
                SEOAICAjaxResponse::alert(esc_html__('Provide at least one headline or one description.', 'seoaic'))->wpSend();
            }

            $groups = [
                [
                    'id'  => $groupId,
                    'ads' => [
                        [
                            'id'           => $adId,
                            'headlines'    => array_values($titles),
                            'descriptions' => array_values($descriptions),
                        ],
                    ],
                ],
            ];
        }

        $groups = $this->sanitize_groups_payload($groups);

        if (empty($groups)) {
            SEOAICAjaxResponse::alert(esc_html__('Groups payload is empty after sanitization.', 'seoaic'))->wpSend();
        }

        $backendResults = $this->sendPerGroupToBackend($campaignId, $groups);

        foreach ($backendResults as $gid => $res) {
            if (is_wp_error($res)) {
                SEOAICAjaxResponse::alert($res->get_error_message())->wpSend();
            }
        }

        SEOAICAjaxResponse::alert('<p>' . __('The ad group has been updated successfully.', 'seoaic') . '</p>')->addFields(['adCopy' => $groups])->wpSend();
    }

    public function ajaxSEACreateAdCopy() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $postId     = !empty($_post['postId']) ? absint($_post['postId']) : 0;
        $startDate  = !empty($_post['startDate']) ? sanitize_text_field($_post['startDate']) : '';
        $endDate    = !empty($_post['endDate']) ? sanitize_text_field($_post['endDate']) : '';
        $budget     = !empty($_post['budget']) ? $_post['budget'] : 0;
        $campaignId = !empty($_post['campaignId']) ? sanitize_text_field($_post['campaignId']) : '';
        $data       = [];

        if (!$postId) {
            SEOAICAjaxResponse::alert(esc_html__('Invalid post ID', 'seoaic'))->wpSend();
        }

        $post = get_post($postId);
        if (!$post) {
            SEOAICAjaxResponse::alert(esc_html__('Unfortunately, the post does not exist.', 'seoaic'))->wpSend();
        }

        $postUrl    = get_permalink($postId);
        $content    = $post->post_content;
        $post_title = $post->post_title;

        $groupsRaw = isset($_post['groups']) ? $_post['groups'] : [];
        if (!is_array($groupsRaw)) {
            $groups = json_decode($groupsRaw, true);
            if (!is_array($groups)) { $groups = []; }
        } else {
            $groups = $groupsRaw;
        }

        $sanitize_string_array = static function ($arr) {
            $arr = (array) $arr;
            $out = [];
            foreach ($arr as $s) {
                $s = sanitize_text_field($s);
                if ($s !== '') { $out[] = $s; }
            }
            return array_values($out);
        };

        $adsData = [];
        foreach ($groups as $g) {
            if (empty($g['ads']) || !is_array($g['ads'])) {
                continue;
            }

            $ad = $g['ads'][0];

            $headlines    = isset($ad['headlines']) ? $sanitize_string_array($ad['headlines']) : [];
            $descriptions = isset($ad['descriptions']) ? $sanitize_string_array($ad['descriptions']) : [];
            $keyword      = isset($ad['keyword']) ? sanitize_text_field($ad['keyword']) : '';

            if (!empty($headlines) || !empty($descriptions)) {
                $adsData[] = [
                    'keyword'      => $keyword,
                    'ads' => [[
                        'headlines'    => $headlines,
                        'descriptions' => $descriptions,
                    ]]
                ];
            }
        }

        $data = [
            'post_id'       => $postId,
            'post_url'      => $postUrl,
            'post_content'  => $content,
            'post_title'    => $post_title,
            'ad_groups'     => $adsData,
            'post_language' => $this->seoaic->multilang->get_post_language($postId),
            'start_date'    => $startDate,
            'end_date'      => $endDate,
        ];

        if (!empty($budget)) {
            $data['budget'] = [
                'type'                   => 'campaign_budget',
                'campaign_budget_amount' => $budget,
            ];
        }

        if (empty($campaignId)) {
            $this->SEAInintCurl("/api/sea/campaigns", 'post', $data, false);
            SEOAICAjaxResponse::alert('<p>' . __('Campaign created successfully. You can view it on the SEA Dashboard: <a href="' . admin_url('admin.php?page=seoaic-sea-dashboard') . '">Open Dashboard</a>', 'seoaic') . '</p>')->wpSend();
        } else {
            $this->SEAInintCurl("/api/sea/campaigns/$campaignId/ad-group/", 'post', $adsData, false);
            SEOAICAjaxResponse::alert('<p>' . __('Campaign updated successfully. You can view it on the SEA Dashboard: <a href="' . admin_url('admin.php?page=seoaic-sea-dashboard') . '">Open Dashboard</a>', 'seoaic') . '</p>')->wpSend();
        }
    }

    private function sendPerGroupToBackend($campaignId, array $groups): array {
        $results = [];

        foreach ($groups as $g) {
            $groupId = $g['id'];
            $ads     = isset($g['ads']) ? $g['ads'] : [];

            foreach ($ads as $ad) {
                $payload = [
                    'ads' => [[
                        'id'          => $ad['id'],
                        'headline'    => isset($ad['headlines']) ? array_values($ad['headlines']) : [],
                        'description' => isset($ad['descriptions']) ? array_values($ad['descriptions']) : [],
                    ]],
                ];

                $endpoint = "/api/sea/campaigns/{$campaignId}/ad-group/{$groupId}";

                $result   = $this->SEAInintCurl($endpoint, 'post', $payload);

                $results[$groupId][$ad['id']] = $result;
            }
        }

        return $results;
    }

    private function sanitize_string_array(array $arr): array {
        $out = [];
        foreach ($arr as $v) {
            if ($v === null) { continue; }
            $v = is_scalar($v) ? (string) $v : '';
            $v = trim($v);
            if ($v === '') { continue; }
            $out[] = sanitize_text_field($v);
        }
        return $out;
    }

    private function sanitize_groups_payload(array $groups): array {
        $clean = [];

        foreach ($groups as $id => $g ) {
            $gid = isset($g['id']) ? sanitize_text_field($g['id']) : $id;

            $ads = isset($g['ads']) && is_array($g['ads']) ? $g['ads'] : [];
            $adsClean = [];

            foreach ($ads as $adId => $ad) {
                $adId = isset($ad['id']) ? sanitize_text_field($ad['id']) : $adId;

                $heads = [];
                if (isset($ad['headlines']) && is_array($ad['headlines'])) {
                    $heads = $this->sanitize_string_array($ad['headlines']);
                } elseif (isset($ad['headline']) && is_array($ad['headline'])) {
                    $heads = $this->sanitize_string_array($ad['headline']);
                }

                $descs = [];
                if (isset($ad['descriptions']) && is_array($ad['descriptions'])) {
                    $descs = $this->sanitize_string_array($ad['descriptions']);
                } elseif (isset($ad['description']) && is_array($ad['description'])) {
                    $descs = $this->sanitize_string_array($ad['description']);
                }

                if (empty($heads) && empty($descs)) { continue; }

                $adsClean[] = [
                    'id'           => $adId,
                    'headlines'    => array_values($heads),
                    'descriptions' => array_values($descs),
                ];
            }

            if (!empty($adsClean)) {
                $clean[] = [
                    'id'  => $gid,
                    'ads' => $adsClean,
                ];
            }
        }

        return $clean;
    }

    // This function creates a new ad copy for a specific SEA campaign.
    public function SEAChangeStatus() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $campaignId = !empty($_post['campaignId']) ? intval($_post['campaignId']) : 0;
        $status = !empty($_post['status']) ? strtoupper(sanitize_text_field($_post['status'])) : '';

        $allowed_statuses = ['UNSPECIFIED', 'UNKNOWN', 'ENABLED', 'PAUSED', 'REMOVED'];

        if ($campaignId <= 0 || empty($status) || !in_array($status, $allowed_statuses, true)) {
            SEOAICAjaxResponse::alert(esc_html__('Invalid campaign ID or status.', 'seoaic'))->wpSend();
        }

        $this->SEAInintCurl("/api/sea/campaigns/$campaignId", 'post', ['status' => $status]);

        SEOAICAjaxResponse::success()->wpSend();
    }

    // This function changes the status of a specific ad group in a SEA campaign.
    public function SEAAdGroupChangeStatus() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $campaignId = !empty($_post['campaignId']) ? intval($_post['campaignId']) : 0;
        $groupId = !empty($_post['groupId']) ? intval($_post['groupId']) : 0;
        $status = !empty($_post['status']) ? strtoupper(sanitize_text_field($_post['status'])) : '';

        $allowed_statuses = ['UNSPECIFIED', 'UNKNOWN', 'ENABLED', 'PAUSED', 'REMOVED'];

        if ($campaignId <= 0 || empty($status) || !in_array($status, $allowed_statuses, true)) {
            SEOAICAjaxResponse::alert(esc_html__('Invalid campaign ID or status.', 'seoaic'))->wpSend();
        }

        $data = [
            'status' => $status
        ];

        $this->SEAInintCurl("/api/sea/campaigns/$campaignId/ad-group/$groupId", 'post', $data);

        SEOAICAjaxResponse::success()->wpSend();
    }

    // This function retrieves the performance data for a specific SEA campaign.
    public function SEAGetPerformance() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $campaignId = !empty($_post['campaignId']) ? intval($_post['campaignId']) : 0;
        $adGroupId = !empty($_post['adGroupId']) ? intval($_post['adGroupId']) : 0;

        if ($campaignId <= 0 || $adGroupId <= 0) {
            SEOAICAjaxResponse::alert(esc_html__('Invalid campaign ID or add group ID.', 'seoaic'))->wpSend();
        }

        $result = $this->SEAInintCurl("/api/sea/campaigns/$campaignId/ad-group/$adGroupId/stats", 'get');

        SEOAICAjaxResponse::success()->addFields(['data' => $result])->wpSend();
    }

    // This function retrieves the campaign data for chart.
    public function SEAGetCampaignStats()
    {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post      = wp_unslash($_POST);
        $dateFrom   = !empty($_post['dateFrom'])   ? sanitize_text_field($_post['dateFrom'])   : '';
        $dateTo     = !empty($_post['dateTo'])     ? sanitize_text_field($_post['dateTo'])     : '';
        $dateSelect = !empty($_post['dateSelect']) ? sanitize_text_field($_post['dateSelect']) : '';
        $interval   = !empty($_post['interval'])   ? sanitize_text_field($_post['interval'])   : 'weekly';

        $status = (isset($_post['status']) && ($v = sanitize_text_field($_post['status'])) !== '' && $v !== 'all') ? $v : '';
        $owner  = (isset($_post['owner'])  && ($v = sanitize_text_field($_post['owner']))  !== '' && $v !== 'all') ? $v : '';

        if (empty($dateFrom) && empty($dateTo) && $dateSelect === 'custom') {
            SEOAICAjaxResponse::alert(esc_html__('Please select a date range.', 'seoaic'))->wpSend();
        }

        if ($dateFrom && $dateTo) {
            $fromIso = $this->normalizeIsoDate($dateFrom);
            $toIso   = $this->normalizeIsoDate($dateTo);

            $range = [
                'dateFrom' => $fromIso,
                'dateTo'   => $toIso,
            ];
        } else {
            $range = (array) $this->getDateRange($dateSelect, $interval);
        }

        $params = array_filter([
            'status'   => ($status === '' ? null : $status),
            'owner'    => ($owner  === '' ? null : $owner),
            'dateFrom' => $range['dateFrom'] ?? null,
            'dateTo'   => $range['dateTo']   ?? null,
        ], static fn($v) => $v !== null && $v !== '');

        $query = http_build_query($params, '', '&', PHP_QUERY_RFC3986);

        $endpoint = '/api/sea/campaigns/stats';
        $url = $endpoint . '?' . $query;

        $result = $this->SEAInintCurl($url, 'get');

        SEOAICAjaxResponse::success()
            ->addFields([
                'siteStats' => $result['data'] ?? [],
            ])
            ->wpSend();
    }

    function getDateRange($option, $rangeDuration)
    {
        $today = new DateTime();

        switch ($option) {
            case 'last_7_days':
                $interval = new DateInterval('P7D');
                break;
            case 'last_28_days':
                $interval = new DateInterval('P28D');
                break;
            case 'last_3_months':
                $interval = new DateInterval('P3M');
                break;
            case 'last_6_months':
                $interval = new DateInterval('P6M');
                break;
            case 'last_12_months':
                $interval = new DateInterval('P1Y');
                break;
            case 'last_16_months':
                $interval = new DateInterval('P16M');
                break;
            default:
                $interval = new DateInterval('P3M');
        }

        $dateFrom = clone $today;
        $dateFrom->sub($interval);

        return [
            'dateFrom' => $dateFrom->format('Y-m-d'),
            'dateTo' => $today->format('Y-m-d'),
        ];
    }

    private function normalizeIsoDate($raw): ?string
    {
        if ($raw === null) return null;

        $raw = trim(wp_unslash((string)$raw));
        if ($raw === '') return null;

        $raw  = preg_replace('/[^\d\.\-\/]/u', '', $raw);
        $norm = preg_replace('/[\.\/\-]+/', '-', $raw);

        $tz = wp_timezone();

        $formats = ['!Y-m-d', '!d.m.Y', '!d-m-Y', '!Y/m/d'];

        foreach ($formats as $fmt) {
            $dt = DateTimeImmutable::createFromFormat($fmt, $norm, $tz);
            if ($dt instanceof DateTimeImmutable) {
                $errors = DateTimeImmutable::getLastErrors() ?: [];
                if (($errors['warning_count'] ?? 0) === 0 && ($errors['error_count'] ?? 0) === 0) {
                    return $dt->format('Y-m-d');
                }
            }
        }

        return null;
    }


    // This function creates a new SEA campaign.
    public function SEAcreateCampaign() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $campaignDataRaw = !empty($_post['campaignData']) ? $_post['campaignData'] : [];
        $campaignData = json_decode($campaignDataRaw, true);
        $postId = !empty($_post['postId']) ? intval($_post['postId']) : 0;
        $budget = !empty($_post['budget']) ? intval($_post['budget']) : null;
        $startDate = !empty($_post['startDate']) ? sanitize_text_field($_post['startDate']) : null;
        $endDate = !empty($_post['endDate']) ? sanitize_text_field($_post['endDate']) : null;
        $campaignId = !empty($_post['campaignId']) ? sanitize_text_field($_post['campaignId']) : null;

        if (empty($campaignData)) {
            SEOAICAjaxResponse::alert(esc_html__('Campaign data is required.', 'seoaic'))->wpSend();
        }

        if (!$postId) {
            SEOAICAjaxResponse::alert(esc_html__('Invalid post ID', 'seoaic'))->wpSend();
        }

        $post = get_post($postId);

        if (!$post) {
            SEOAICAjaxResponse::alert(esc_html__('Unfortunately, the post does not exist.', 'seoaic'))->wpSend();
        };

        $postUrl = get_permalink($postId);
        $content = $post->post_content;
        $post_title = $post->post_title;

        $budget = [
            "type" => "campaign_budget",
            "campaign_budget_amount" => $budget
        ];

        $data = [
            "post_id"       => $postId ,
            "post_url"      => $postUrl,
            "post_content"  => $content,
            "post_title"    => $post_title,
            "post_keywords" => $campaignData,
            "post_language" => $this->seoaic->multilang->get_post_language($postId),
            "budget"        => $budget,
            "start_date"    => $startDate,
            "end_date"      => $endDate,
            'campaign_id'   => $campaignId
        ];

        $result = $this->SEAInintCurl("/api/sea/campaigns/generate-ad-copies", 'post', $data, false);
        $result['campaignData'] = $data;

        if (!empty($result['status']) && $result['status'] === 'success') {
            SEOAICAjaxResponse::success()->addFields($result)->wpSend();
        }

        if (!empty($result['response_code']) && intval($result['response_code']) === 443) {
            SEOAICAjaxResponse::alert($result['message'])->wpSend();
        }

        SEOAICAjaxResponse::success()->addFields($result)->wpSend();
    }

    // This function retrieves the details of a specific SEA campaign.
    public function SEAEditCampaign() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $campaignId = !empty($_post['campaignId']) ? intval($_post['campaignId']) : 0;
        $text = !empty($_post['text']) ? $_post['text'] : '';
        $avg = !empty($_post['avg']) ? $_post['avg'] : 0;
        $budget = !empty($_post['budget']) ? $_post['budget'] : 0;
        $startSate = !empty($_post['startDate']) ? sanitize_text_field($_post['startDate']) : null;
        $rawEnd = !empty($_post['endDate']) ? sanitize_text_field($_post['endDate']) : '';

        $endDate = (
            $rawEnd !== '' &&
            !preg_match('/^-+$/', $rawEnd) &&
            $this->is_valid_date($rawEnd, ['Y-m-d'])
        ) ? $rawEnd : '';

        if (empty($campaignId)) {
            SEOAICAjaxResponse::alert(esc_html__('Invalid campaign ID.', 'seoaic'))->wpSend();
        }

        $data = [
            'text'       => $text,
            'avg'        => $avg,
            'budget'     => ['campaign_budget_amount' => $budget],
            "start_date" => $startSate,
            "end_date"   => $endDate
        ];

        $this->SEAInintCurl("/api/sea/campaigns/$campaignId", 'post', $data);

        SEOAICAjaxResponse::success()->addFields($data)->wpSend();
    }

    function is_valid_date(string $str, array $formats = ['Y-m-d']): bool {
        foreach ($formats as $fmt) {
            $dt = DateTime::createFromFormat('!' . $fmt, $str);
            $errors = DateTime::getLastErrors();
            if (
                $dt instanceof DateTime &&
                ($errors['warning_count'] ?? 0) === 0 &&
                ($errors['error_count'] ?? 0) === 0
            ) {
                return true;
            }
        }
        return false;
    }

    // This function retrieves the details of a specific SEA campaign.
    public function SEAGetAccounts() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $result = $this->SEAInintCurl('/api/sea/accounts', 'get', [], false);

        SEOAICAjaxResponse::success()->addFields(['accounts' => $result['data']])->wpSend();
    }

    // This function sets the SEA account for the user.
    public function SEASetAccount() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $customerId = !empty($_post['customerId']) ? sanitize_text_field($_post['customerId']) : '';
        $loginCustomerId = !empty($_post['loginCustomerId']) ? sanitize_text_field($_post['loginCustomerId']) : '';

        $data = ['customer_id' => $customerId, 'login_customer_id' => $loginCustomerId];

        $result = $this->SEAInintCurl("/api/sea/accounts", 'post', $data, false);

        if (!empty($result) && $result['status'] ===  'success') {
            SEOAICAjaxResponse::redirect()->redirectTo(admin_url('admin.php?page=seoaic-sea-dashboard'))->wpSend();
        }

        SEOAICAjaxResponse::success()->wpSend();
    }

    // This function retrieves the ad group details for a specific SEA campaign.
    public function SEAGetCampaignGroup() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $campaignId = !empty($_post['campaignId']) ? sanitize_text_field($_post['campaignId']) : '';

        if (empty($campaignId)) {
            SEOAICAjaxResponse::alert(esc_html__('Invalid campaign ID.', 'seoaic'))->wpSend();
        }

        $result = $this->SEAInintCurl("/api/sea/campaigns/$campaignId", 'get');

        SEOAICAjaxResponse::success()->addFields($result['data'] ?? [])->wpSend();
    }

    function SEAGetCampaignByPostId() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $postId = !empty($_post['postId']) ? sanitize_text_field($_post['postId']) : 0;

        if (empty($postId)) {
            SEOAICAjaxResponse::alert(esc_html__('Invalid post ID.', 'seoaic'))->wpSend();
        }

        $result = $this->SEAInintCurl("/api/sea/campaigns/by-post/$postId", 'get');
        $result['postId'] = $postId;

        SEOAICAjaxResponse::success()->addFields(['groups' => $result ?? [], 'seaDashboard' => admin_url('admin.php?page=seoaic-sea-dashboard')])->wpSend();
    }

    function SEAUnauthorized() {
        global $SEOAIC_OPTIONS;
        delete_user_meta(get_current_user_id(), 'seoaic_sea_auth_key');
        $SEOAIC_OPTIONS['seoaicSeaAuthToken'] = null;
        update_option('seoaic_options', $SEOAIC_OPTIONS);
        SEOAICAjaxResponse::redirect()->redirectTo(admin_url('admin.php?page=seoaic-sea-dashboard'))->wpSend();
    }

    public function handle_auth_callback() {
        if ( isset($_GET['page']) && $_GET['page'] === 'seoaic-sea-auth-success' ) {
            global $SEOAIC_OPTIONS;

            $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
            $current_url = esc_url_raw( $current_url );

            $SEAUserTokens = $this->get_sea_tokens($current_url, ['sea_user_token','sea_admin_token']);

            if (!empty($SEAUserTokens['sea_admin_token']) && !empty($SEAUserTokens['sea_user_token'])) {
                $userToken  = $SEAUserTokens['sea_user_token'];
                $adminToken = $SEAUserTokens['sea_admin_token'];

                add_user_meta(get_current_user_id(), 'seoaic_sea_auth_key', $adminToken, true);
                $SEOAIC_OPTIONS['seoaicSeaAuthToken'] = $userToken;
                update_option('seoaic_options', $SEOAIC_OPTIONS);
            }

        }
    }

    public function get_sea_tokens(string $url, array $params): array {
        $parts = parse_url($url);
        if (!isset($parts['query'])) {
            return array_fill_keys($params, null);
        }

        parse_str($parts['query'], $query);

        $result = [];
        foreach ($params as $key) {
            $result[$key] = null;

            if (!empty($query[$key])) {
                $token = trim($query[$key]);
                if (preg_match('/^[A-Za-z0-9_\-\.]+$/', $token)) {
                    $result[$key] = $token;
                }
            }
        }

        return $result;
    }

    function SEALogOut() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);
        $this->SEAUnauthorized();
    }

    function getSeaAuthToken() {
        global $SEOAIC_OPTIONS;

        $adminToken = get_user_meta(get_current_user_id(), 'seoaic_sea_auth_key', true);
        $globalToken = !empty($SEOAIC_OPTIONS['seoaicSeaAuthToken']) ? $SEOAIC_OPTIONS['seoaicSeaAuthToken'] : '';

        $tokenArray = [
            'token'      => '',
            'is_owner'   => false
        ];

        if (!empty($adminToken)) {
            $tokenArray['token'] = $adminToken;
            $tokenArray['is_owner'] = true;
        } else {
            $tokenArray['token'] = $globalToken;
        }

        return $tokenArray;
    }

    function SEASetSettings() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $adCopyGuidelines = !empty($_post['prompt_ad_copy_guideline']) ? sanitize_textarea_field($_post['prompt_ad_copy_guideline']) : '';
        $utmSource = !empty($_post['utm_source']) ? sanitize_text_field($_post['utm_source']) : '';
        $utmMedium = !empty($_post['utm_medium']) ? sanitize_text_field($_post['utm_medium']) : '';
        $utmCampaign = !empty($_post['utm_campaign']) ? sanitize_text_field($_post['utm_campaign']) : '';
        $utmContent = !empty($_post['utm_content']) ? sanitize_text_field($_post['utm_content']) : '';
        $utmEnabled = !empty($_post['utm_enabled']) && $_post['utm_enabled'] === '1' ? true : false;

        $campaign_conversion_tracking = !empty($_post['conversion_tracking']) && $_post['conversion_tracking'] === '1' ? true : false;
        $campaign_default_goal = !empty($_post['default_goal']) ? sanitize_text_field($_post['default_goal']) : '';
        $campaign_default_bid_strategy = !empty($_post['default_bid_strategy']) ? sanitize_text_field($_post['default_bid_strategy']) : '';
        $campaign_default_countries = ! empty( $_post['default_countries'] )
            ? array_map(
                function( $id ) {
                    return $id;
                },
                (array) $_post['default_countries']
            )
            : [];

        $data = [
            'data' => [
                [
                    "key"   => "prompt_ad_copy_guideline",
                    "value" => $adCopyGuidelines
                ],
                [
                    "key"   => "utm_enabled",
                    "value" => $utmEnabled
                ],
                [
                    "key"   => "utm_source",
                    "value" => $utmSource
                ],
                [
                    "key"   => "utm_medium",
                    "value" => $utmMedium
                ],
                [
                    "key"   => "utm_campaign",
                    "value" => $utmCampaign
                ],
                [
                    "key"   => "utm_content",
                    "value" => $utmContent
                ],
                [
                    "key"   => "campaign_conversion_tracking",
                    "value" => $campaign_conversion_tracking
                ],
                [
                    "key"   => "campaign_default_goal",
                    "value" => $campaign_default_goal
                ],
                [
                    "key"   => "campaign_default_bid_strategy",
                    "value" => $campaign_default_bid_strategy
                ],
                [
                    "key"   => "campaign_default_countries",
                    "value" => $campaign_default_countries
                ],
            ]
        ];

        $result = $this->SEAInintCurl("/api/blog-settings/sea", 'post', $data, false);

        SEOAICAjaxResponse::success()->addFields($result)->wpSend();
    }

    public function SEAGetSettings() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $result = $this->SEAInintCurl("/api/blog-settings/sea", 'get');
        $rows   = !empty($result) && is_array($result) ? $result : [];

        $defaultCountryIds = [];
        foreach ($rows as $row) {
            if (!empty($row['key']) && $row['key'] === 'campaign_default_countries') {
                $defaultCountryIds = $this->parse_ids_mixed($row['value'] ?? []);
                break;
            }
        }

        if (!empty($defaultCountryIds)) {
            $idsQuery = implode('&', array_map(
                static fn($id) => 'ids=' . rawurlencode($id),
                $defaultCountryIds
            ));

            $locationArray = $this->SEAInintCurl("/api/sea/campaigns/get-geo-targets?{$idsQuery}", 'get');
            if (!empty($locationArray['status']) && $locationArray['status'] === 'success') {
                $result['locations'] = $locationArray['data'];
            }
        }

        SEOAICAjaxResponse::success()->addFields($result)->wpSend();
    }

    private function parse_ids_mixed($raw): array {
        if (is_array($raw)) {
            $items = $raw;
        } elseif (is_string($raw)) {
            $raw = trim($raw);
            if ($raw === '') {
                $items = [];
            } else {
                $decoded = json_decode($raw, true);
                if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                    $items = $decoded;
                } else {
                    $items = array_map('trim', explode(',', $raw));
                }
            }
        } else {
            $items = [];
        }

        $items = array_values(array_filter(array_map('strval', $items), static function($v){
            return $v !== '';
        }));

        return array_values(array_unique($items));
    }

    function SEASearchLocations() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);
        $q = isset($_post['q']) ? sanitize_text_field( wp_unslash($_post['q']) ) : '';
        $exclude = isset($_POST['exclude']) ? (array) $_POST['exclude'] : [];

        $result = $this->SEAInintCurl("/api/sea/campaigns/search-geo-targets?query=$q", 'get');

        $items = array_values(array_filter($result['data'], function ($i) use ($q, $exclude) {
            if (in_array($i['id'], $exclude, true)) return false;
            return $q === '' || stripos($i['name'], $q) !== false || stripos($i['id'], $q) !== false;
        }));

        SEOAICAjaxResponse::success()->addFields(['items' => $items])->wpSend();
    }

    public function SEARefreshAdGroup() {
        check_ajax_referer(SeoaicAjaxValidation::ACTION_STRING);

        $_post = wp_unslash($_POST);

        $groupKey     = isset($_post['groupKey']) ? sanitize_text_field($_post['groupKey']) : '';
        $keyword      = isset($_post['keyword']) ? sanitize_text_field($_post['keyword']) : '';
        $customPrompt = isset($_post['customPrompt']) ? sanitize_textarea_field($_post['customPrompt']) : '';
        $postId       = isset($_post['postID']) ? absint($_post['postID']) : 0;

        $headlinesRaw    = isset($_post['headlines']) ? (string) $_post['headlines'] : '[]';
        $descriptionsRaw = isset($_post['descriptions']) ? (string) $_post['descriptions'] : '[]';

        $headlinesIn  = json_decode($headlinesRaw, true);
        $descsIn      = json_decode($descriptionsRaw, true);

        if (!$postId) {
            SEOAICAjaxResponse::alert(esc_html__('Invalid post ID', 'seoaic'))->wpSend();
        }

        $post = get_post($postId);
        if (!$post) {
            SEOAICAjaxResponse::alert(esc_html__('Unfortunately, the post does not exist.', 'seoaic'))->wpSend();
        }

        $content    = (string) $post->post_content;
        $post_title = (string) $post->post_title;

        if (!is_array($headlinesIn)) { $headlinesIn = []; }
        if (!is_array($descsIn))     { $descsIn = []; }

        $sanitize_items = static function(array $arr): array {
            $out = [];
            foreach ($arr as $item) {
                if (!is_array($item)) { continue; }
                $id   = isset($item['id'])   ? sanitize_text_field($item['id'])   : '';
                $text = isset($item['text']) ? sanitize_text_field($item['text']) : '';
                if ($id === '' || $text === '') { continue; }
                $out[] = compact('id','text');
            }
            return $out;
        };
        $headlinesIn = $sanitize_items($headlinesIn);
        $descsIn     = $sanitize_items($descsIn);

        $only_texts = static function(array $arr): array {
            $out = [];
            foreach ($arr as $item) {
                if (is_array($item) && isset($item['text'])) {
                    $out[] = sanitize_text_field($item['text']);
                }
            }
            return $out;
        };
        $headlinesInOleg = $only_texts($headlinesIn);
        $descsInOleg     = $only_texts($descsIn);

        $data = [
            'post_content'  => $content,
            'post_title'    => $post_title,
            'post_keyword'  => $keyword,
            'post_language' => $this->seoaic->multilang->get_post_language($postId),
            'user_prompt'   => $customPrompt,
            'headlines'     => $headlinesInOleg,
            'descriptions'  => $descsInOleg,
        ];

        $result = $this->SEAInintCurl("/api/sea/campaigns/improve-ad-copy", 'post', $data, false);

        $apiData   = is_array($result) && isset($result['data']) && is_array($result['data']) ? $result['data'] : [];
        $apiHeads  = isset($apiData['headlines']) && is_array($apiData['headlines']) ? $apiData['headlines'] : [];
        $apiDescs  = isset($apiData['descriptions']) && is_array($apiData['descriptions']) ? $apiData['descriptions'] : [];

        $norm = static function(string $s): string {
            $s = wp_strip_all_tags($s);
            $s = preg_replace('/\s+/u', ' ', $s ?? '');
            return mb_strtolower(trim((string)$s), 'UTF-8');
        };

        $build_map = static function(array $pairs) use ($norm): array {
            $map = [];
            foreach ($pairs as $p) {
                if (!is_array($p)) { continue; }
                $old = isset($p['old']) ? sanitize_text_field($p['old']) : '';
                $new = isset($p['new']) ? sanitize_text_field($p['new']) : '';
                if ($old === '' || $new === '') { continue; }
                $key = $norm($old);
                if ($key !== '' && !isset($map[$key])) {
                    $map[$key] = $new;
                }
            }
            return $map;
        };

        $headsMap = $build_map($apiHeads);
        $descsMap = $build_map($apiDescs);

        $map_suggestions = static function(array $uiItems, array $map, callable $norm): array {
            $out = [];
            foreach ($uiItems as $it) {
                $id   = $it['id'];
                $text = $it['text'];
                $key  = $norm($text);
                if ($key !== '' && isset($map[$key])) {
                    $suggestion = $map[$key];
                    if ($norm($suggestion) === $key) { continue; }
                    $out[] = [
                        'id'         => $id,
                        'old'        => $text,
                        'suggestion' => $suggestion,
                    ];
                }
            }
            return $out;
        };

        $suggestHeads = $map_suggestions($headlinesIn, $headsMap, $norm);
        $suggestDescs = $map_suggestions($descsIn,     $descsMap, $norm);

        SEOAICAjaxResponse::success()
            ->addFields([
                'groupKey'      => $groupKey,
                'improvedData'  => $apiData,
                'headlines'     => $suggestHeads,
                'descriptions'  => $suggestDescs,
            ])
            ->wpSend();
    }
}
