<?php

class Am_Newsletter_Plugin_Mailwizz extends Am_Newsletter_Plugin
{
    function _initSetupForm(Am_Form_Setup $form)
    {
        $el = $form->addText('public_key', ['class' => 'am-el-wide'])->setLabel("Mailwizz Public Key");
        $el->addRule(   'regex',
                        'API Keys must be in form of 40 hex lowercase signs',
                        '/^[a-f0-9]{40}$/');
        $el->addRule('required');

        $el = $form->addSecretText('private_key', ['class' => 'am-el-wide'])->setLabel("Mailwizz Private Key");
        $el->addRule(   'regex',
                        'API Keys must be in form of 40 hex lowercase signs',
                        '/^[a-f0-9]{40}$/');
        $el->addRule('required');

        $el = $form->addText('url', ['class' => 'am-el-wide'])
            ->setLabel("Mailwizz API URL");
        $el->addRule('callback', 'The URL isn\'t valid', function($url) {
            if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) {
                return false;
            }

            return true;
        });
        $el->addRule('required');
        $form->addAdvcheckbox('debug')->setLabel("Debug mode");
    }

    function isConfigured()
    {
        return $this->getConfig('public_key') && $this->getConfig('private_key') && $this->getConfig('url');
    }

    /**
     * Make authenticated API request to MailWizz
     * 
     * @param string $endpoint API endpoint path
     * @param array $data Request data
     * @param string $method HTTP method (GET, POST, PUT, DELETE)
     * @param int[] $allowedStatuses HTTP status codes to treat as success (e.g. [409] for "already exists")
     * @return array Response data
     * @throws Exception
     */
    protected function makeApiRequest($endpoint, $data = [], $method = 'GET', array $allowedStatuses = [])
    {
        $url = rtrim($this->getConfig('url'), '/') . $endpoint;
        $request = new Am_HttpRequest($url, $method);
        
        // Generate signature and set headers
        $this->signRequest($request, $data, $method);
        
        // Add request data
        // MailWizz API expects application/x-www-form-urlencoded for POST/PUT body, not JSON
        if (!empty($data)) {
            if ($method === 'GET') {
                $query_string = http_build_query($data, '', '&');
                if (!empty($query_string)) {
                    $url .= '?' . $query_string;
                    $request->setUrl($url);
                }
            } else {
                $request->setBody(http_build_query($data, '', '&'));
                $request->setHeader('Content-Type', 'application/x-www-form-urlencoded');
            }
        }
        
        $response = $request->send();
        $this->debug($request, $response, $url);
        
        $status = $response->getStatus();
        $treatAsSuccess = ($status >= 200 && $status < 300) || in_array($status, $allowedStatuses, true);
        
        if (!$treatAsSuccess) {
            throw new Exception('API request failed: ' . $status . ' ' . $response->getReasonPhrase());
        }
        
        $response_data = json_decode($response->getBody(), true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            $response_data = [];
        }
        
        return $response_data;
    }

    /**
     * Sign the request according to MailWizz API specification
     * 
     * @param Am_HttpRequest $request
     * @param array $data Request data
     * @param string $method HTTP method
     */
    protected function signRequest(Am_HttpRequest $request, $data = [], $method = 'GET')
    {
        $publicKey = $this->getConfig('public_key');
        $privateKey = $this->getConfig('private_key');
        $timestamp = time();
        $remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
        
        // Set special header parameters
        $specialHeaderParams = [
            'X-MW-PUBLIC-KEY' => $publicKey,
            'X-MW-TIMESTAMP' => $timestamp,
            'X-MW-REMOTE-ADDR' => $remoteAddr,
        ];
        
        // Add headers to request
        foreach ($specialHeaderParams as $key => $value) {
            $request->setHeader($key, $value);
        }
        
        // Merge special headers with request data for signature
        $params = array_merge($specialHeaderParams, $data);
        
        // Sort parameters alphabetically
        ksort($params, SORT_STRING);
        
        // Build signature string: METHOD URL?PARAMS
        $requestUrl = $request->getUrl();
        $separator = strpos($requestUrl, '?') !== false ? '&' : '?';
        $signatureString = strtoupper($method) . ' ' . $requestUrl . $separator . http_build_query($params, '', '&');
        
        // Generate HMAC-SHA1 signature
        $signature = hash_hmac('sha1', $signatureString, $privateKey, false);
        
        // Add signature header
        $request->setHeader('X-MW-SIGNATURE', $signature);
        // Content-Type is set in makeApiRequest (form-urlencoded for POST/PUT body)
    }

    /**
     * Find subscriber_uid by email via MailWizz search-by-email endpoint
     *
     * @param string $listId
     * @param string $email
     * @return string|null subscriber_uid or null if not found
     */
    protected function findSubscriberUidByEmail($listId, $email)
    {
        try {
            $response = $this->makeApiRequest(
                "/lists/{$listId}/subscribers/search-by-email",
                ['EMAIL' => $email],
                'GET'
            );
        } catch (Exception $e) {
            return null; // 404 or other — subscriber not in list
        }
        if (empty($response['data']['subscriber_uid'])) {
            return null;
        }
        return $response['data']['subscriber_uid'];
    }

    public function changeSubscription(User $user, array $addLists, array $deleteLists)
    {
        // Add subscribers to lists (409 "already exists" is treated as OK)
        foreach ($addLists as $list_id) {
            try {
                $this->makeApiRequest("/lists/{$list_id}/subscribers", [
                    'EMAIL' => $user->email,
                    'FNAME' => $user->name_f,
                    'LNAME' => $user->name_l
                ], 'POST', [409]);
            } catch (Exception $e) {
                $this->getDi()->logger->error("Failed to add subscriber to list {$list_id}: " . $e->getMessage());
                return false;
            }
        }

        // Remove subscribers from lists (MailWizz DELETE requires subscriber_uid in URL)
        foreach ($deleteLists as $list_id) {
            try {
                $subscriberUid = $this->findSubscriberUidByEmail($list_id, $user->email);
                if ($subscriberUid !== null) {
                    $this->makeApiRequest("/lists/{$list_id}/subscribers/{$subscriberUid}", [], 'DELETE');
                }
                // If not found, consider already removed
            } catch (Exception $e) {
                $this->getDi()->logger->error("Failed to remove subscriber from list {$list_id}: " . $e->getMessage());
                return false;
            }
        }

        return true;
    }

    public function getLists()
    {
        $ret = [];

        try {
            $response = $this->makeApiRequest('/lists', [
                'page' => 1,
                'per_page' => 999999
            ], 'GET');

            if (!empty($response['data']['records'])) {
                foreach ($response['data']['records'] as $el) {
                    $ret[$el['general']['list_uid']] = [
                        'title' => $el['general']['display_name'],
                    ];
                }
            }
        } catch (Exception $e) {
            $this->getDi()->logger->error("Failed to get lists: " . $e->getMessage());
        }
        return $ret;
    }

}